
#include "PerceptronModel.h"

// [*] and_or_tree::CFileNameList

void and_or_tree::CFileNameList::myInit(string strPN_Base, string strFN_Suffix, int FileIndexStart, int FileIndexWidth, int FileAmount)
{
	int i;
	char strTemp[10001];

	this->FileAmount = FileAmount;
	TitleList.Clear();
	FullFileNameList.Clear();
	for ( i = 0; i < FileAmount; ++i )
	{
		if (FileIndexWidth < 0)
			sprintf_s(strTemp, "%d", FileIndexStart+i);
		else
			sprintf_s(strTemp, "0*d", FileIndexWidth, FileIndexStart+i);
		TitleList.PushBack(strTemp);
		sprintf_s(strTemp, "%s%s%s", strPN_Base.c_str(), strTemp, strFN_Suffix.c_str());
		FullFileNameList.PushBack(strTemp);
	}
}

void and_or_tree::CFileNameList::myInit(string strFN_FileNameList)
{
	ifstream inFile(strFN_FileNameList.c_str());

	string strPN_Base;
	string strFN_Suffix;

	getline(inFile, strPN_Base);
	getline(inFile, strFN_Suffix);
	inFile>>FileAmount;

	int i;
	string strTitle;
	char strTemp[10001];

	TitleList.Clear();
	FullFileNameList.Clear();
	for ( i = 0; i < FileAmount; ++i )
	{
		inFile>>strTitle;
		TitleList.PushBack(strTitle);
		sprintf_s(strTemp, "%s%s%s", strPN_Base.c_str(), strTitle.c_str(), strFN_Suffix.c_str());
		FullFileNameList.PushBack(strTemp);
	}

	inFile.clear();
	inFile.close();
}

string and_or_tree::CFileNameList::Title(int FileIndex) const
{
	if (FileIndex<0 || FileIndex>=FileAmount) return "";
	return TitleList.pElement[FileIndex];
}

string and_or_tree::CFileNameList::FullFileName(int FileIndex) const
{
	if (FileIndex<0 || FileIndex>=FileAmount) return "";
	return FullFileNameList.pElement[FileIndex];
}

// [*] and_or_tree::CPerceptronModel

double and_or_tree::CPerceptronModel::S_InstanceMaxScale;
double and_or_tree::CPerceptronModel::S_InstanceMaxRotation;
double and_or_tree::CPerceptronModel::S_BrothersMaxScaleDifference;
double and_or_tree::CPerceptronModel::S_BrothersMaxRotationDifference;
int and_or_tree::CPerceptronModel::S_MaxChoiceCandidateAmount;
int and_or_tree::CPerceptronModel::S_AppearanceFeatureBinAmount;
double and_or_tree::CPerceptronModel::S_DefaultTargetRecall;
int and_or_tree::CPerceptronModel::S_ChoiceLowLimit;
int and_or_tree::CPerceptronModel::S_ChoiceHighLimit;
int and_or_tree::CPerceptronModel::S_TrainRoundAmount;
int and_or_tree::CPerceptronModel::S_PerNegImageInstanceAmountForTrain;
int and_or_tree::CPerceptronModel::S_TestPrintAmountPerImageForTest;
double and_or_tree::CPerceptronModel::S_MAXIMUM_SCORE;
double and_or_tree::CPerceptronModel::S_IMAGE_BOUNDARY;

void and_or_tree::CPerceptronModel::InitDefaultParameters()
{
	S_MAXIMUM_SCORE = +1e20;
	S_IMAGE_BOUNDARY = +5.0;

	S_InstanceMaxScale = 0.50;
	S_InstanceMaxRotation = PI / 4;
	S_BrothersMaxScaleDifference = 0.70;
	S_BrothersMaxRotationDifference = PI / 4;
	S_MaxChoiceCandidateAmount = 50000;

	S_AppearanceFeatureBinAmount = 50;

	S_DefaultTargetRecall = 0.99;
	S_ChoiceLowLimit = 50;
	S_ChoiceHighLimit = 64;

	S_TrainRoundAmount = 10;
	S_PerNegImageInstanceAmountForTrain = 1;

	S_TestPrintAmountPerImageForTest = 1;

	CParsedInstance::S_MinPositionDifference = 5.0;
	CParsedInstance::S_MinRotationDifference = PI / 6;
	CParsedInstance::S_MinScaleDifference = 0.20;
}

bool and_or_tree::CPerceptronModel::LoadFromFile(string strFN_Model)
{
	ifstream inFile(strFN_Model.c_str());
	bool ret = InputFromStream(inFile);
	inFile.clear();
	inFile.close();
	return ret;
}

bool and_or_tree::CPerceptronModel::InputFromStream(istream& inStream)
{
	int i, j, j_dim, tt;
	double *pd;

	StandardNodeInstanceList.Resize(pTree->NodeAmount);
	for ( i = 0; i < pTree->NodeAmount; ++i )
		if (!StandardNodeInstanceList.pElement[i].CBasicInstance::InputFromStream(inStream))
			return false;
	_BuildStandardNodeInstanceList();

	StandardLeafNodeOrientationList.Resize(pTree->NodeAmount);
	for ( i = 0; i < pTree->NodeAmount; ++i )
		if (!(inStream>>StandardLeafNodeOrientationList.pElement[i]))
			return false;

	if (!(inStream>>ScoreScaler)) return false;

	if (!(inStream>>tt) || tt!=CoefficientAmount_Shape) return false;
	for ( i = 0; i < CoefficientAmount_Shape; ++i )
		inStream>>CoefficientList_Shape.pElement[i];

	if (!(inStream>>tt) || tt!=CoefficientAmount_Appearance) return false;
	for ( i = 0; i < CoefficientAmount_Appearance; ++i )
		inStream>>CoefficientList_Appearance.pElement[i];

	if (!(inStream>>tt) || tt!=FeatureDimension_Shape) return false;
	for ( i = 0; i < FeatureDimension_Shape; ++i )
	{
		inStream>>FeatureMeanList_Shape.pElement[i];
		inStream>>FeatureVarianceList_Shape.pElement[i];
	}

	if (!(inStream>>tt) || tt!=FeatureDimension_Appearance) return false;
	for ( i = 0; i < FeatureDimension_Appearance; ++i )
	{
		if (!(FeatureHistogramConfigList_Appearance.pElement[i].InputFromStream(inStream))) return false;
		j_dim = FeatureHistogramConfigList_Appearance.pElement[i].BinAmount;
		FeatureBinValueList_Appearance.pElement[i].Resize(j_dim);
		pd = FeatureBinValueList_Appearance.pElement[i].pElement;
		for ( j = 0; j < j_dim; ++j, ++pd )
			inStream>>*pd;
	}

	NodeParsingScoreThresholdList.Resize(pTree->NodeAmount);
	for ( i = 0; i < pTree->NodeAmount; ++i )
		if (!(inStream>>NodeParsingScoreThresholdList.pElement[i]))
			return false;

	return true;
}

void and_or_tree::CPerceptronModel::TranslateFromYuanhaoModel(string strFN_YuanhaoModel)
{
	char strTemp[10000];

	ifstream inFile(strFN_YuanhaoModel.c_str());

	int _null_i, _rule_amount, _my_leaf_amount, _or_node_amount;
	double _null_f, _gamma, _leaf_ori;
	int i, j, o;
	double* p_sf_mean;
	double* p_sf_var;
	double* p_sf_coe;

	double* p_af_coe;
	CHistogramConfig* p_af_hist_config;
	CSimpleTypeArray<double>* p_list_af_bin_value;
	double* p_af_bin_value;
	int af_bin_amount;
	double af_min, af_max;

	inFile>>_null_i>>_null_i>>_rule_amount;

	p_sf_mean = FeatureMeanList_Shape.pElement;
	p_sf_var = FeatureVarianceList_Shape.pElement;
	p_sf_coe = CoefficientList_Shape.pElement;

	this->ScoreScaler = 1.0;

	inFile>>_null_i;
	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		const CAbstractNode* pNode = pTree->GetNode(i);
		int ChildrenAmount = pNode->GetChildrenAmount();

		inFile>>_null_i>>_null_i>>_null_i>>_null_i;
		for ( j = 0; j < _rule_amount; ++j ) inFile>>_null_f;
		if (pNode->GetType() == CAbstractNode::TYPE_AND)
		{
			inFile>>_null_i>>_null_i>>_null_i;
			inFile>>_null_i;
			inFile>>_null_i>>_null_i>>_null_i;
			for ( j = 0; j < ChildrenAmount; ++j ) for ( o = 0; o < ChildrenAmount; ++o ) inFile>>_null_f;
			for ( j = 0; j < ChildrenAmount; ++j ) for ( o = 0; o < ChildrenAmount; ++o ) inFile>>_null_f;
			inFile>>_null_i;
			inFile>>_null_i>>_null_i>>_null_i>>_null_i>>_null_i>>_null_i>>_gamma;
			inFile>>_null_i;

			_gamma /= pNode->GetShapeFeatureDimension();
			for ( j = 0; j < pNode->GetShapeFeatureDimension(); ++j, ++p_sf_coe )
				*p_sf_coe = _gamma;

			for ( j = 0; j < pNode->GetShapeFeatureDimension(); ++j, ++p_sf_mean )
				inFile>>*p_sf_mean;

			for ( j = 0; j < pNode->GetShapeFeatureDimension(); ++j, ++p_sf_var )
				inFile>>*p_sf_var;

			inFile>>_my_leaf_amount;
			for ( j = 0; j < _my_leaf_amount; ++j )
				inFile>>_null_i>>_null_f>>_null_f;
		}
	}

	inFile>>_or_node_amount;
	for ( i = 0; i < _or_node_amount; ++i )
		inFile>>_null_i>>_null_i>>_null_i>>_null_f;

	p_af_coe = CoefficientList_Appearance.pElement;
	p_af_hist_config = FeatureHistogramConfigList_Appearance.pElement;
	p_list_af_bin_value = FeatureBinValueList_Appearance.pElement;

	inFile>>_null_i;
	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		const CAbstractNode* pNode = pTree->GetNode(i);
		int ChildrenAmount = pNode->GetChildrenAmount();

		if (pNode->GetType() != CAbstractNode::TYPE_LEAF) continue;

		inFile>>_null_i>>_leaf_ori>>_null_i;
		sprintf_s(strTemp, "Leaf %2d:  Ori = %.3lf vs %.3lf", i, StandardLeafNodeOrientationList.pElement[i], _leaf_ori);
		cout<<strTemp<<endl;
		StandardLeafNodeOrientationList.pElement[i] = CMyMath::FormatAngle_Small(_leaf_ori);
		
		for ( j = 0; j < pNode->GetAppearanceFeatureDimension(); ++j, ++p_af_coe, ++p_af_hist_config, ++p_list_af_bin_value )
		{
			inFile>>*p_af_coe>>af_max>>af_min>>af_bin_amount>>_null_f;
			p_af_hist_config->myInit(af_bin_amount, false, af_min, af_max);

			p_list_af_bin_value->Resize(af_bin_amount);
			p_af_bin_value = p_list_af_bin_value->pElement;
			for ( o = 0; o < af_bin_amount; ++o, ++p_af_bin_value )
				inFile>>*p_af_bin_value;
		}
	}

	inFile.clear();
	inFile.close();
}

void and_or_tree::CPerceptronModel::SaveToFile(string strFN_Model) const
{
	ofstream outFile(strFN_Model.c_str());
	OutputToStream(outFile);
	outFile.clear();
	outFile.close();
}

void and_or_tree::CPerceptronModel::OutputToStream(ostream& outStream) const
{
	int i, j, j_dim;
	const double *qd;


	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		StandardNodeInstanceList.pElement[i].CBasicInstance::OutputToStream(outStream, false);
		outStream<<endl;
	}

	outStream.precision(16);

	for ( i = 0; i < pTree->NodeAmount; ++i )
		outStream<<"\t"<<showpos<<scientific<<StandardLeafNodeOrientationList.pElement[i]<<endl;

	outStream<<showpos<<scientific<<ScoreScaler<<endl;

	outStream<<CoefficientAmount_Shape<<endl;
	for ( i = 0; i < CoefficientAmount_Shape; ++i )
		outStream<<"\t"<<showpos<<scientific<<CoefficientList_Shape.pElement[i]<<endl;

	outStream<<CoefficientAmount_Appearance<<endl;
	for ( i = 0; i < CoefficientAmount_Appearance; ++i )
		outStream<<"\t"<<showpos<<scientific<<CoefficientList_Appearance.pElement[i]<<endl;

	outStream<<FeatureDimension_Shape<<endl;
	for ( i = 0; i < FeatureDimension_Shape; ++i )
	{
		outStream<<"\t"<<showpos<<scientific<<FeatureMeanList_Shape.pElement[i];
		outStream<<"\t"<<showpos<<scientific<<FeatureVarianceList_Shape.pElement[i];
		outStream<<endl;
	}

	outStream<<FeatureDimension_Appearance<<endl;
	for ( i = 0; i < FeatureDimension_Appearance; ++i )
	{
		outStream<<"\t";
		FeatureHistogramConfigList_Appearance.pElement[i].OutputToStream(outStream);
		j_dim = FeatureHistogramConfigList_Appearance.pElement[i].BinAmount;
		qd = FeatureBinValueList_Appearance.pElement[i].pElement;
		for ( j = 0; j < j_dim; ++j, ++qd )
			outStream<<"\t"<<showpos<<scientific<<*qd;
		outStream<<endl;
	}

	for ( i = 0; i < pTree->NodeAmount; ++i )
		outStream<<"\t"<<showpos<<scientific<<NodeParsingScoreThresholdList.pElement[i]<<endl;
}

void and_or_tree::CPerceptronModel::ConvertShapeFeature(CSimpleTypeArray<double>& DstShapeFeatureList, int NodeIndex) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;

	double t, td, tv;
	int j;
	int jbase = FeatureOffsetList_Shape.pElement[NodeIndex];
	int jdim = pTree->PtrNodeList.pElement[NodeIndex]->GetShapeFeatureDimension();
	double* pd = DstShapeFeatureList.pElement;
	const double* qm = FeatureMeanList_Shape.pElement + jbase;
	const double* qv = FeatureVarianceList_Shape.pElement + jbase;

	for ( j = 0; j < jdim; ++j, ++pd, ++qm, ++qv )
	{
		td = *pd - *qm;

		if (j < 6)
			td = CMyMath::FormatAngle(td);

		tv = *qv;
		
		if (tv < EPS)
		{
			*pd = 0.0;
			continue;
		}

		t = 0.0;
		t -= CMyMath::sqr(td) / CMyMath::sqr(tv) * 0.5;
		t -= log(tv * sqrt(PI+PI));

		*pd = t;
	}
}

void and_or_tree::CPerceptronModel::ConvertAppearanceFeature(CSimpleTypeArray<double>& DstAppearanceFeatureList, int NodeIndex) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;

	double t;
	int j, o;
	int jbase = FeatureOffsetList_Appearance.pElement[NodeIndex];
	int jdim = pTree->PtrNodeList.pElement[NodeIndex]->GetAppearanceFeatureDimension();
	double* pd = DstAppearanceFeatureList.pElement;
	const CHistogramConfig* qHistogramConfig = FeatureHistogramConfigList_Appearance.pElement + jbase;
	const CSimpleTypeArray<double>* qBinValueList = FeatureBinValueList_Appearance.pElement + jbase;

	for ( j = 0; j < jdim; ++j, ++pd, ++qHistogramConfig, ++qBinValueList )
	{
		o = qHistogramConfig->SelectBin_Nearest(*pd);
		t = qBinValueList->pElement[o];
		*pd = t;
	}
}

void and_or_tree::CPerceptronModel::ConvertTreeFeature_SingleInstance(
	CSimpleTypeArray<double>& DstConvertedShapeFeatureList,
	CSimpleTypeArray<double>& DstConvertedAppearanceFeatureList,
	const CSimpleTypeArray<double>& SrcOriginalShapeFeatureList,
	const CSimpleTypeArray<double>& SrcOriginalAppearanceFeatureList
	)
{
	int i, j;
	double* pd;
	double* pShapeFeature;
	double* pAppearanceFeature;
	const double* qShapeFeature;
	const double* qAppearanceFeature;
	int CurrentShapeFeatureDim, CurrentAppearanceFeatureDim;
	const CAbstractNode* qNode;

	CSimpleTypeArray<double> ShapeFeatureList;
	CSimpleTypeArray<double> AppearanceFeatureList;

	DstConvertedShapeFeatureList.Resize(SrcOriginalShapeFeatureList.Size);
	DstConvertedAppearanceFeatureList.Resize(SrcOriginalAppearanceFeatureList.Size);

	pShapeFeature = DstConvertedShapeFeatureList.pElement;
	pAppearanceFeature = DstConvertedAppearanceFeatureList.pElement;
	qShapeFeature = SrcOriginalShapeFeatureList.pElement;
	qAppearanceFeature = SrcOriginalAppearanceFeatureList.pElement;
	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		qNode = pTree->GetNode(i);

		CurrentShapeFeatureDim = qNode->GetShapeFeatureDimension();
		ShapeFeatureList.Resize(CurrentShapeFeatureDim);
		pd = ShapeFeatureList.pElement;
		for ( j = 0; j < CurrentShapeFeatureDim; ++j )
			*(pd++) = *(qShapeFeature++);
		this->ConvertShapeFeature(ShapeFeatureList, i);
		pd = ShapeFeatureList.pElement;
		for ( j = 0; j < CurrentShapeFeatureDim; ++j )
			*(pShapeFeature++) = *(pd++);

		CurrentAppearanceFeatureDim = qNode->GetAppearanceFeatureDimension();
		AppearanceFeatureList.Resize(CurrentAppearanceFeatureDim);
		pd = AppearanceFeatureList.pElement;
		for ( j = 0; j < CurrentAppearanceFeatureDim; ++j )
			*(pd++) = *(qAppearanceFeature++);
		this->ConvertAppearanceFeature(AppearanceFeatureList, i);
		pd = AppearanceFeatureList.pElement;
		for ( j = 0; j < CurrentAppearanceFeatureDim; ++j )
			*(pAppearanceFeature++) = *(pd++);
	}
}

double and_or_tree::CPerceptronModel::ComputePerceptronScore(
	int CoefficientOffset,
	const CSimpleTypeArray<double>& CoefficientList,
	const double* FeatureList,
	int Dimension
	)
{
	if (CoefficientOffset<0 || CoefficientOffset>=(int)CoefficientList.Size)
		CoefficientOffset = (int)CoefficientList.Size;
	const double* qh = CoefficientList.pElement + CoefficientOffset;
	if (Dimension > (int)CoefficientList.Size - CoefficientOffset)
		Dimension = (int)CoefficientList.Size - CoefficientOffset;
	int i;
	double ret = 0;
	for ( i = 0; i < Dimension; ++i )
		ret += *(qh++) * *(FeatureList++);
	return ret;
}

double and_or_tree::CPerceptronModel::ComputeShapeScore(int NodeIndex, const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList, size_t Offset) const
{
	double ret = 0.0;

	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return ret;

	int jdim = pTree->PtrNodeList.pElement[NodeIndex]->GetShapeFeatureDimension();

	ret = ScoreScaler * ComputePerceptronScore(
		CoefficientOffsetList_Shape.pElement[NodeIndex],
		CoefficientList_Shape,
		SrcConvertedShapeFeatureList.pElement + Offset,
		jdim
		);

	return ret;
}

double and_or_tree::CPerceptronModel::ComputeAppearanceScore(int NodeIndex, const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList, size_t Offset) const
{
	double ret = 0.0;

	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return ret;

	int jdim = pTree->PtrNodeList.pElement[NodeIndex]->GetAppearanceFeatureDimension();

	ret = ScoreScaler * ComputePerceptronScore(
		CoefficientOffsetList_Appearance.pElement[NodeIndex],
		CoefficientList_Appearance,
		SrcConvertedAppearanceFeatureList.pElement + Offset,
		jdim
		);

	return ret;
}

void and_or_tree::CPerceptronModel::ComputeNodeConvertedShapeFeature(
	CSimpleTypeArray<double>& DstConvertedShapeFeatureList,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CParsedInstance* pParsedInstance
	) const
{
	DstConvertedShapeFeatureList.Clear();
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.ComputeNodeShapeFeature(
		DstConvertedShapeFeatureList,
		NodeIndex, pParsedInstance);
	this->ConvertShapeFeature(DstConvertedShapeFeatureList, NodeIndex);
}

void and_or_tree::CPerceptronModel::ComputeNodeConvertedShapeFeature(
	CSimpleTypeArray<double>& DstConvertedShapeFeatureList,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CInstanceChoice* pInstanceChoice
	) const
{
	DstConvertedShapeFeatureList.Clear();
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.ComputeNodeShapeFeature(
		DstConvertedShapeFeatureList,
		NodeIndex, pInstanceChoice);
	this->ConvertShapeFeature(DstConvertedShapeFeatureList, NodeIndex);
}

void and_or_tree::CPerceptronModel::ComputeNodeConvertedAppearanceFeature(
	CSimpleTypeArray<double>& DstConvertedAppearanceFeatureList,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CParsedInstance* pParsedInstance
	) const
{
	DstConvertedAppearanceFeatureList.Clear();
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.ComputeNodeAppearanceFeature(
		DstConvertedAppearanceFeatureList,
		NodeIndex, pParsedInstance);
	this->ConvertAppearanceFeature(DstConvertedAppearanceFeatureList, NodeIndex);
}

void and_or_tree::CPerceptronModel::ComputeNodeConvertedAppearanceFeature(
	CSimpleTypeArray<double>& DstConvertedAppearanceFeatureList,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CInstanceChoice* pInstanceChoice
	) const
{
	DstConvertedAppearanceFeatureList.Clear();
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.ComputeNodeAppearanceFeature(
		DstConvertedAppearanceFeatureList,
		NodeIndex, pInstanceChoice);
	this->ConvertAppearanceFeature(DstConvertedAppearanceFeatureList, NodeIndex);
}

void and_or_tree::CPerceptronModel::ComputeNodeConvertedFullFeature(
	CSimpleTypeArray<double>& DstConvertedShapeFeatureList,
	CSimpleTypeArray<double>& DstConvertedAppearanceFeatureList,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CParsedInstance* pParsedInstance
	) const
{
	DstConvertedShapeFeatureList.Clear();
	DstConvertedAppearanceFeatureList.Clear();
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.ComputeNodeFullFeature(
		DstConvertedShapeFeatureList,
		DstConvertedAppearanceFeatureList,
		NodeIndex, pParsedInstance);
	this->ConvertShapeFeature(DstConvertedShapeFeatureList, NodeIndex);
	this->ConvertAppearanceFeature(DstConvertedAppearanceFeatureList, NodeIndex);
}

void and_or_tree::CPerceptronModel::ComputeNodeConvertedFullFeature(
	CSimpleTypeArray<double>& DstConvertedShapeFeatureList,
	CSimpleTypeArray<double>& DstConvertedAppearanceFeatureList,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CInstanceChoice* pInstanceChoice
	) const
{
	DstConvertedShapeFeatureList.Clear();
	DstConvertedAppearanceFeatureList.Clear();
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.ComputeNodeFullFeature(
		DstConvertedShapeFeatureList,
		DstConvertedAppearanceFeatureList,
		NodeIndex, pInstanceChoice);
	this->ConvertShapeFeature(DstConvertedShapeFeatureList, NodeIndex);
	this->ConvertAppearanceFeature(DstConvertedAppearanceFeatureList, NodeIndex);
}

void and_or_tree::CPerceptronModel::ComputeNodeShapeScore(
	CParsedInstance* pDstParsedInstance,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList
	) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.RefreshNodeShapeScore(
		this->ComputeShapeScore(NodeIndex, SrcConvertedShapeFeatureList),
		NodeIndex, pDstParsedInstance);
}

void and_or_tree::CPerceptronModel::ComputeNodeShapeScore(
	CInstanceChoice* pDstInstanceChoice,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList
	) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.RefreshNodeShapeScore(
		this->ComputeShapeScore(NodeIndex, SrcConvertedShapeFeatureList),
		NodeIndex, pDstInstanceChoice);
}

void and_or_tree::CPerceptronModel::ComputeNodeAppearanceScore(
	CParsedInstance* pDstParsedInstance,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.RefreshNodeAppearanceScore(
		this->ComputeAppearanceScore(NodeIndex, SrcConvertedAppearanceFeatureList),
		NodeIndex, pDstParsedInstance);
}

void and_or_tree::CPerceptronModel::ComputeNodeAppearanceScore(
	CInstanceChoice* pDstInstanceChoice,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.RefreshNodeAppearanceScore(
		this->ComputeAppearanceScore(NodeIndex, SrcConvertedAppearanceFeatureList),
		NodeIndex, pDstInstanceChoice);
}

void and_or_tree::CPerceptronModel::ComputeNodeTotalScore(
	CParsedInstance* pDstParsedInstance,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.RefreshNodeTotalScore(
		this->ComputeShapeScore(NodeIndex, SrcConvertedShapeFeatureList),
		this->ComputeAppearanceScore(NodeIndex, SrcConvertedAppearanceFeatureList),
		NodeIndex, pDstParsedInstance);
}

void and_or_tree::CPerceptronModel::ComputeNodeTotalScore(
	CInstanceChoice* pDstInstanceChoice,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	TreeInstance.RefreshNodeTotalScore(
		this->ComputeShapeScore(NodeIndex, SrcConvertedShapeFeatureList),
		this->ComputeAppearanceScore(NodeIndex, SrcConvertedAppearanceFeatureList),
		NodeIndex, pDstInstanceChoice);
}

void and_or_tree::CPerceptronModel::ComputeTreeParsedInstanceTotalScore(CTreeInstance& TreeInstance) const
{
	int i;

	CSimpleTypeArray<double> ShapeFeatureList;
	CSimpleTypeArray<double> AppearanceFeatureList;

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		//if (i == 20)
		//	i = i;
		this->ComputeNodeConvertedFullFeature(ShapeFeatureList, AppearanceFeatureList, TreeInstance, i, TreeInstance.ParsedInstanceList.pElement + i);
		this->ComputeNodeTotalScore(TreeInstance.ParsedInstanceList.pElement + i, TreeInstance, i, ShapeFeatureList, AppearanceFeatureList);
	}
}

void and_or_tree::CPerceptronModel::ComputeTreeInstanceChoiceTotalScore(CTreeInstance& TreeInstance) const
{
	int i, j;
	CInstanceChoice* qic;

	CSimpleTypeArray<double> ShapeFeatureList;
	CSimpleTypeArray<double> AppearanceFeatureList;

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		CSimpleTypeArray<CInstanceChoice>& CurrentChoiceList = TreeInstance.GetInstanceList(i);
		qic = CurrentChoiceList.pElement;

		for ( j = (int)CurrentChoiceList.Size-1; j >= 0; --j, ++qic )
		{
			this->ComputeNodeConvertedFullFeature(ShapeFeatureList, AppearanceFeatureList, TreeInstance, i, qic);
			this->ComputeNodeTotalScore(qic, TreeInstance, i, ShapeFeatureList, AppearanceFeatureList);
		}
	}
}

void and_or_tree::CPerceptronModel::ComputeTreeTotalScore_ByFeature(
	CTreeInstance& TreeInstance,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	int i, j;
	const CAbstractNode* qNode;
	int ChildrenAmount;
	const int* qi;

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		qNode = pTree->GetNode(i);
		ChildrenAmount = qNode->GetChildrenAmount();
		qi = qNode->GetPtrChildrenIndex();
		CParsedInstance& cur_pi = TreeInstance.ParsedInstanceList.pElement[i];

		cur_pi.tree_shape_score = cur_pi.node_shape_score = 
			ComputeShapeScore(i, SrcConvertedShapeFeatureList, FeatureOffsetList_Shape.pElement[i]);
		cur_pi.tree_appearance_score = cur_pi.node_appearance_score = 
			ComputeAppearanceScore(i, SrcConvertedAppearanceFeatureList, FeatureOffsetList_Appearance.pElement[i]);
		for ( j = 0; j < ChildrenAmount; ++j )
		{
			cur_pi.tree_shape_score += TreeInstance.ParsedInstanceList.pElement[qi[j]].tree_shape_score;
			cur_pi.tree_appearance_score += TreeInstance.ParsedInstanceList.pElement[qi[j]].tree_appearance_score;
		}
		cur_pi.node_total_score = cur_pi.node_shape_score + cur_pi.node_appearance_score;
		cur_pi.tree_total_score = cur_pi.tree_shape_score + cur_pi.tree_appearance_score;
	}
}

void and_or_tree::CPerceptronModel::ComputeTreeTotalScore_ByFeature(
	CSimpleTypeArray<double>& DstTreeTotalScoreList,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	CSimpleTypeArray<double> ns;
	CSimpleTypeArray<double> na;
	CSimpleTypeArray<double> nt;
	CSimpleTypeArray<double> ts;
	CSimpleTypeArray<double> ta;

	ComputeTreeTotalScore_ByFeature(ns, na, nt, ts, ta, DstTreeTotalScoreList, SrcConvertedShapeFeatureList, SrcConvertedAppearanceFeatureList);
}

void and_or_tree::CPerceptronModel::ComputeTreeTotalScore_ByFeature(
	CSimpleTypeArray<double>& DstNodeShapeScoreList,
	CSimpleTypeArray<double>& DstNodeAppearanceScoreList,
	CSimpleTypeArray<double>& DstNodeTotalScoreList,
	CSimpleTypeArray<double>& DstTreeShapeScoreList,
	CSimpleTypeArray<double>& DstTreeAppearanceScoreList,
	CSimpleTypeArray<double>& DstTreeTotalScoreList,
	const CSimpleTypeArray<double>& SrcConvertedShapeFeatureList,
	const CSimpleTypeArray<double>& SrcConvertedAppearanceFeatureList
	) const
{
	int i, j;
	const CAbstractNode* qNode;
	int ChildrenAmount;
	const int* qi;

	DstNodeShapeScoreList.Resize(pTree->NodeAmount);
	DstNodeAppearanceScoreList.Resize(pTree->NodeAmount);
	DstNodeTotalScoreList.Resize(pTree->NodeAmount);
	DstTreeShapeScoreList.Resize(pTree->NodeAmount);
	DstTreeAppearanceScoreList.Resize(pTree->NodeAmount);
	DstTreeTotalScoreList.Resize(pTree->NodeAmount);

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		qNode = pTree->GetNode(i);
		ChildrenAmount = qNode->GetChildrenAmount();
		qi = qNode->GetPtrChildrenIndex();

		DstTreeShapeScoreList.pElement[i] = DstNodeShapeScoreList.pElement[i] = 
			ComputeShapeScore(i, SrcConvertedShapeFeatureList, FeatureOffsetList_Shape.pElement[i]);
		DstTreeAppearanceScoreList.pElement[i] = DstNodeAppearanceScoreList.pElement[i] = 
			ComputeAppearanceScore(i, SrcConvertedAppearanceFeatureList, FeatureOffsetList_Appearance.pElement[i]);
		for ( j = 0; j < ChildrenAmount; ++j )
		{
			DstTreeShapeScoreList.pElement[i] += DstTreeShapeScoreList.pElement[qi[j]];
			DstTreeAppearanceScoreList.pElement[i] += DstTreeAppearanceScoreList.pElement[qi[j]];
		}
		DstNodeTotalScoreList.pElement[i] = DstNodeShapeScoreList.pElement[i] + DstNodeAppearanceScoreList.pElement[i];
		DstTreeTotalScoreList.pElement[i] = DstTreeShapeScoreList.pElement[i] + DstTreeAppearanceScoreList.pElement[i];
	}
}

void and_or_tree::CPerceptronModel::MakePosShapeFeatureFileName(char* DstFileName, const char* strPN_Training, int PosFileIndex)
{
	sprintf(DstFileName, "%sPos.%04d.Feature.Shape.dat", strPN_Training, PosFileIndex);
}

void and_or_tree::CPerceptronModel::MakePosAppearanceFeatureFileName(char* DstFileName, const char* strPN_Training, int PosFileIndex)
{
	sprintf(DstFileName, "%sPos.%04d.Feature.Appearance.dat", strPN_Training, PosFileIndex);
}

bool and_or_tree::CPerceptronModel::_ExtractInternalNodeParsedInstance(
	CParsedInstance* pDstParsedInstance,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<const CParsedInstance*>& ChildrenPiptrList,
	const CSimpleTypeArray<bool>& AreChildrenFakeList) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return false;
	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);
	int ChildrenAmount = pNode->GetChildrenAmount();
	const int* qci = pNode->GetPtrChildrenIndex();

	pDstParsedInstance->ClearAll();

	const CParsedInstance * * qqpi;
	int i, j;
	CRichInstance TempRichInstance;
	TempRichInstance.Init_CC_Pairs(ChildrenPiptrList);
	const CRichInstance& StdRichInstance = StandardNodeInstanceList.pElement[NodeIndex];

	const double* qo = TempRichInstance.OrientationList.pElement;
	const double* ql = TempRichInstance.LengthList.pElement;
	const double* ro = StdRichInstance.OrientationList.pElement;
	const double* rl = StdRichInstance.LengthList.pElement;
	int t_offset;

	double t_x, t_y;
	double t_rotation, t_ori;
	double t_scale, t_exp_scale, t_len;
	int plus_amount, miss_children_amount;

	t_rotation = t_scale = 0.0;
	plus_amount = miss_children_amount = 0;
	for ( qqpi = ChildrenPiptrList.pElement, i = 0; i < ChildrenAmount; ++i, ++qqpi )
	{
		if (AreChildrenFakeList.pElement[i])
		{
			++miss_children_amount;
			continue;
		}

		//++plus_amount;
		//t_rotation += (*qqpi)->rotation;
		//t_scale += (*qqpi)->scale;

		for ( j = i+1; j < ChildrenAmount; ++j )
		{
			if (AreChildrenFakeList.pElement[j]) continue;

			t_offset = TempRichInstance.ComputeOffset_ChildrenPair(i, j);

			++plus_amount;
			t_rotation += CMyMath::FormatAngle(qo[t_offset] - ro[t_offset]);
			t_scale += log(ql[t_offset] / rl[t_offset]);
		}
	}

	if (plus_amount < 1) return false;
	if (miss_children_amount > 1) return false;

	pDstParsedInstance->rotation = t_rotation / plus_amount;
	pDstParsedInstance->scale = t_scale / plus_amount;
	t_exp_scale = exp(pDstParsedInstance->scale);

	t_x = t_y = 0.0;
	plus_amount = 0;
	for ( qqpi = ChildrenPiptrList.pElement, i = 0; i < ChildrenAmount; ++i, ++qqpi )
	{
		if (AreChildrenFakeList.pElement[i]) continue;

		t_offset = TempRichInstance.ComputeOffset_SingleChild(i);

		t_ori = ro[t_offset] + pDstParsedInstance->rotation;
		t_len = rl[t_offset] * t_exp_scale;

		++plus_amount;
		t_x += (*qqpi)->x + t_len * cos(t_ori);
		t_y += (*qqpi)->y + t_len * sin(t_ori);
	}

	pDstParsedInstance->x = t_x / plus_amount;
	pDstParsedInstance->y = t_y / plus_amount;

	if (pDstParsedInstance->x<S_IMAGE_BOUNDARY || pDstParsedInstance->x>TreeInstance.W-S_IMAGE_BOUNDARY || 
		pDstParsedInstance->y<S_IMAGE_BOUNDARY || pDstParsedInstance->y>TreeInstance.H-S_IMAGE_BOUNDARY)
		return false;

	return true;
}

bool and_or_tree::CPerceptronModel::ExtractInternalNodeParsedInstance(CTreeInstance& TreeInstance, int NodeIndex) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return false;
	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);
	int ChildrenAmount = pNode->GetChildrenAmount();
	const int* qci = pNode->GetPtrChildrenIndex();

	CSimpleTypeArray<const CParsedInstance*> TempPiptrList;
	TempPiptrList.Resize(ChildrenAmount);
	TempPiptrList.Fill(NULL);

	CSimpleTypeArray<bool> AreChildrenFakeList;
	AreChildrenFakeList.Resize(ChildrenAmount);
	AreChildrenFakeList.Fill(false);

	const CParsedInstance * * qqpi;
	int i;
	for ( qqpi = TempPiptrList.pElement, i = 0; i < ChildrenAmount; ++i, ++qqpi )
		*qqpi = TreeInstance.ParsedInstanceList.pElement + qci[i];

	bool ret = _ExtractInternalNodeParsedInstance(
		TreeInstance.ParsedInstanceList.pElement + NodeIndex,
		TreeInstance, NodeIndex, TempPiptrList, AreChildrenFakeList);

	return ret;
}

bool and_or_tree::CPerceptronModel::ExtractInternalNodeInstanceChoice(
	CInstanceChoice* pDstInstanceChoice,
	const CTreeInstance& TreeInstance, int NodeIndex,
	const CSimpleTypeArray<int> ChildrenChoiceIndexList) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return false;
	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);
	int ChildrenAmount = pNode->GetChildrenAmount();
	const int* qci = pNode->GetPtrChildrenIndex();
	const int* qcci = ChildrenChoiceIndexList.pElement;

	CSimpleTypeArray<const CParsedInstance*> TempPiptrList;
	TempPiptrList.Resize(ChildrenAmount);
	TempPiptrList.Fill(NULL);

	CSimpleTypeArray<bool> AreChildrenFakeList;
	AreChildrenFakeList.Resize(ChildrenAmount);
	AreChildrenFakeList.Fill(false);

	bool fHasFakeChildren = false;
	const CParsedInstance * * qqpi;
	int i;
	for ( qqpi = TempPiptrList.pElement, i = 0; i < ChildrenAmount; ++i, ++qqpi )
	{
		if (qcci[i] < 0)
		{
			*qqpi = TreeInstance.ParsedInstanceList.pElement + qci[i];
			AreChildrenFakeList.pElement[i] = true;
			fHasFakeChildren = true;
			continue;
		}
		*qqpi = TreeInstance.GetInstanceList(qci[i]).pElement + qcci[i];
	}

	bool ret = _ExtractInternalNodeParsedInstance(pDstInstanceChoice, TreeInstance, NodeIndex, TempPiptrList, AreChildrenFakeList);

	pDstInstanceChoice->fIsfake = false;
	pDstInstanceChoice->fHasFakeChildren = fHasFakeChildren;
	pDstInstanceChoice->ChildrenChoiceIndexList = ChildrenChoiceIndexList;

	return ret;
}

void and_or_tree::CPerceptronModel::ExtractLeafNodeParsedInstance(CParsedInstance* pDstParsedInstance, const CTreeInstance& TreeInstance, int NodeIndex, int xy) const
{
	TreeInstance.BuildPixelInstance(pDstParsedInstance, true, xy);
	pDstParsedInstance->rotation = CMyMath::FormatAngle_Small(pDstParsedInstance->rotation - StandardLeafNodeOrientationList.pElement[NodeIndex]);
	pDstParsedInstance->scale = 0.0;
}

void and_or_tree::CPerceptronModel::ExtractLeafNodeInstanceChoice(CInstanceChoice* pDstIntanceChoice, const CTreeInstance& TreeInstance, int NodeIndex, int xy) const
{
	ExtractLeafNodeParsedInstance(pDstIntanceChoice, TreeInstance, NodeIndex, xy);
	pDstIntanceChoice->fIsfake = false;
	pDstIntanceChoice->fHasFakeChildren = false;
	pDstIntanceChoice->ChildrenChoiceIndexList.Clear();
}

void and_or_tree::CPerceptronModel::ExtractLeafNodeParsedInstance(CParsedInstance* pDstParsedInstance, const CTreeInstance& TreeInstance, int NodeIndex, double x, double y) const
{
	TreeInstance.BuildPixelInstance(pDstParsedInstance, true, x, y);
	pDstParsedInstance->rotation = CMyMath::FormatAngle_Small(pDstParsedInstance->rotation - StandardLeafNodeOrientationList.pElement[NodeIndex]);
	pDstParsedInstance->scale = 0.0;
}

void and_or_tree::CPerceptronModel::ExtractLeafNodeInstanceChoice(CInstanceChoice* pDstIntanceChoice, const CTreeInstance& TreeInstance, int NodeIndex, double x, double y) const
{
	ExtractLeafNodeParsedInstance(pDstIntanceChoice, TreeInstance, NodeIndex, x, y);
	pDstIntanceChoice->fIsfake = false;
	pDstIntanceChoice->fHasFakeChildren = false;
	pDstIntanceChoice->ChildrenChoiceIndexList.Clear();
}

void and_or_tree::CPerceptronModel::_GenerateChildrenChoiceList(
	CSimpleTypeArray<CInstanceChoice>& DstInstanceChoiceList,
	CSimpleTypeArray<int> ChildrenChoiceIndexList,
	CTreeInstance& TreeInstance, int NodeIndex, int MaxCandidateAmount,
	int ChildrenAmount, const int* pChildrenNodeIndex, const CRichInstance& StdRichInstance,
	int CurrentDepth) const
{
	if ((int)DstInstanceChoiceList.Size >= MaxCandidateAmount) return;

	if (CurrentDepth >= ChildrenAmount)
	{
		CInstanceChoice tic;
		bool ok = true;

		ok = ok && this->ExtractInternalNodeInstanceChoice(&tic, TreeInstance, NodeIndex, ChildrenChoiceIndexList);

		ok = ok && (fabs(tic.rotation) < S_InstanceMaxRotation);
		ok = ok && (fabs(tic.scale) < S_InstanceMaxScale);

		if (ok)
			DstInstanceChoiceList.PushBack(tic);
	}
	else
	{
		int i, j, t_offset;
		int ChildNodeIndex = pChildrenNodeIndex[CurrentDepth];
		const CSimpleTypeArray<CInstanceChoice>& ChildrenChoiceInstanceList = TreeInstance.GetInstanceList(ChildNodeIndex);
		double t_len, t_ori;
		double scale_diff, ori_diff;
		const int* qqj;
		const CInstanceChoice* qic_i;

		qic_i = ChildrenChoiceInstanceList.pElement;
		for ( i = 0; i < (int)ChildrenChoiceInstanceList.Size; ++i, ++qic_i )
		{
			if (qic_i->fIsfake) continue;

			qqj = ChildrenChoiceIndexList.pElement;
			for ( j = 0; j < CurrentDepth; ++j, ++qqj )
			{
				if (*qqj < 0) continue;
				const CInstanceChoice& ic_j = TreeInstance.GetInstance(pChildrenNodeIndex[j], *qqj);

				ori_diff = fabs(CMyMath::FormatAngle(qic_i->rotation - ic_j.rotation));
				scale_diff = fabs(qic_i->scale - ic_j.scale);
				if (ori_diff>S_BrothersMaxRotationDifference || scale_diff>S_BrothersMaxScaleDifference) break;

				t_ori = CMyMath::FormatAngle(atan2(qic_i->y - ic_j.y, qic_i->x - ic_j.x));
				t_len = sqrt(CMyMath::sqr(qic_i->x - ic_j.x) + CMyMath::sqr(qic_i->y - ic_j.y)); if (t_len < 1.0) t_len = 1.0;
				t_offset = StdRichInstance.ComputeOffset_ChildrenPair(j, CurrentDepth);
				ori_diff = fabs(CMyMath::FormatAngle(t_ori - StdRichInstance.OrientationList.pElement[t_offset]));
				scale_diff = fabs(log(t_len / StdRichInstance.LengthList.pElement[t_offset]));
				if (ori_diff>S_BrothersMaxRotationDifference || scale_diff>S_BrothersMaxScaleDifference) break;
			}
			if (j < CurrentDepth) continue;

			ChildrenChoiceIndexList.pElement[CurrentDepth] = i;
			_GenerateChildrenChoiceList(
				DstInstanceChoiceList,
				ChildrenChoiceIndexList,
				TreeInstance, NodeIndex, MaxCandidateAmount,
				ChildrenAmount, pChildrenNodeIndex, StdRichInstance,
				CurrentDepth + 1);
		}

		ChildrenChoiceIndexList.pElement[CurrentDepth] = -1;
		_GenerateChildrenChoiceList(
			DstInstanceChoiceList,
			ChildrenChoiceIndexList,
			TreeInstance, NodeIndex, MaxCandidateAmount,
			ChildrenAmount, pChildrenNodeIndex, StdRichInstance,
			CurrentDepth + 1);
	}
}

void and_or_tree::CPerceptronModel::_GenerateInternalNodeInstanceCandidateList(CTreeInstance& TreeInstance, int NodeIndex, int MaxCandidateAmount) const
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;

	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);
	int ChildrenAmount = pNode->GetChildrenAmount();
	const int* pChildrenNodeIndex = pNode->GetPtrChildrenIndex();

	CSimpleTypeArray<int> ChildrenChoiceIndexList;
	ChildrenChoiceIndexList.Resize(ChildrenAmount);
	ChildrenChoiceIndexList.Fill(-1);

	_GenerateChildrenChoiceList(
		TreeInstance.GetInstanceList(NodeIndex),
		ChildrenChoiceIndexList,
		TreeInstance, NodeIndex, S_MaxChoiceCandidateAmount,
		ChildrenAmount, pChildrenNodeIndex, this->StandardNodeInstanceList.pElement[NodeIndex],
		0);
}

bool and_or_tree::CPerceptronModel::_FillMissingTreeAndComputeScore(
	CInstanceChoice* pDstInstanceChoice,
	CTreeInstance& TreeInstance, int NodeIndex,
	bool fComputeScore, bool fReserveFakeInstance
	) const
{
	bool ret = true;
	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);
	int ChildrenAmount = pNode->GetChildrenAmount();
	const int* pChildrenNodeIndex = pNode->GetPtrChildrenIndex();

	if (pDstInstanceChoice->fIsfake)
	{
		pDstInstanceChoice->ChildrenChoiceIndexList.Resize(ChildrenAmount);
		pDstInstanceChoice->ChildrenChoiceIndexList.Fill(-1);
		pDstInstanceChoice->fHasFakeChildren = (ChildrenAmount > 0);
		//if (!(pDstInstanceChoice->fHasFakeChildren)) return ret;
	}
	
	const CRichInstance& StdRichInstance = this->StandardNodeInstanceList.pElement[NodeIndex];
	const double* ro = StdRichInstance.OrientationList.pElement;
	const double* rl = StdRichInstance.LengthList.pElement;
	int t_offset;
	double t_exp_scale, t_ori, t_len;

	int i, j;
	CInstanceChoice tic;
	int* qcci;
	CSimpleTypeArray<double> sf_list;
	CSimpleTypeArray<double> af_list;

	CSimpleTypeArray<bool> fAreChildrenFake;
	fAreChildrenFake.Resize(ChildrenAmount);
	fAreChildrenFake.Fill(false);

	pDstInstanceChoice->fHasFakeChildren = false;
	t_exp_scale = exp(pDstInstanceChoice->scale);
	for ( qcci = pDstInstanceChoice->ChildrenChoiceIndexList.pElement, i = 0; i < ChildrenAmount; ++i, ++qcci )
	{
		if (*qcci >= 0) continue;

		t_offset = StdRichInstance.ComputeOffset_SingleChild(i);
		t_ori = ro[t_offset] + PI + pDstInstanceChoice->rotation;
		t_len = rl[t_offset] * t_exp_scale;

		tic.ClearAll();
		tic.fIsfake = true;
		//tic.fHasFakeChildren = false;
		tic.x = pDstInstanceChoice->x + t_len * cos(t_ori);
		tic.y = pDstInstanceChoice->y + t_len * sin(t_ori);

		if (tic.x<S_IMAGE_BOUNDARY || tic.x>TreeInstance.W-S_IMAGE_BOUNDARY || tic.y<S_IMAGE_BOUNDARY || tic.y>TreeInstance.H-S_IMAGE_BOUNDARY) ret = false;

		tic.rotation = pDstInstanceChoice->rotation;
		tic.scale = pDstInstanceChoice->scale;
		if (pTree->GetNode(pChildrenNodeIndex[i])->GetType() == CAbstractNode::TYPE_LEAF) tic.scale = 0.0;

		CSimpleTypeArray<CInstanceChoice>& dst_icl = TreeInstance.GetInstanceList(pChildrenNodeIndex[i]);
		for ( j = 0; j < (int)dst_icl.Size; ++j )
		{
			if (!CParsedInstance::AreSameByInstance(tic, dst_icl.pElement[j]))
				continue;
			*qcci = j;
			break;
		}
		if (*qcci < 0)
		{
			pDstInstanceChoice->fHasFakeChildren = true;
			fAreChildrenFake.pElement[i] = true;

			*qcci = (int)dst_icl.Size;
			dst_icl.PushBack(tic);

			ret = ret && _FillMissingTreeAndComputeScore(dst_icl.pElement + *qcci, TreeInstance, pChildrenNodeIndex[i], fComputeScore, fReserveFakeInstance);
		}
	}

	if (fComputeScore)
	{
		//if (NodeIndex==20 && fabs(pDstInstanceChoice->x - 189)<0.5 && fabs(pDstInstanceChoice->y - 96)<0.5)
		//	ret &= 1;
		if (ret)
		{
			//this->ComputeNodeConvertedShapeFeature(sf_list, TreeInstance, NodeIndex, pDstInstanceChoice);
			//this->ComputeNodeShapeScore(pDstInstanceChoice, TreeInstance, NodeIndex, sf_list);
			this->ComputeNodeConvertedFullFeature(sf_list, af_list, TreeInstance, NodeIndex, pDstInstanceChoice);
			this->ComputeNodeTotalScore(pDstInstanceChoice, TreeInstance, NodeIndex, sf_list, af_list);
		}
		else
		{
			//TreeInstance.RefreshNodeShapeScore(-S_MAXIMUM_SCORE, NodeIndex, pDstInstanceChoice);
			TreeInstance.RefreshNodeTotalScore(-S_MAXIMUM_SCORE, -S_MAXIMUM_SCORE, NodeIndex, pDstInstanceChoice);
			//pDstInstanceChoice->node_shape_score = pDstInstanceChoice->node_appearance_score = -S_MAXIMUM_SCORE;
			//pDstInstanceChoice->tree_shape_score = pDstInstanceChoice->tree_appearance_score = -S_MAXIMUM_SCORE;
			//pDstInstanceChoice->RefreshTotalScore();
		}
	}

	if (fReserveFakeInstance) return ret;

	for ( qcci = pDstInstanceChoice->ChildrenChoiceIndexList.pElement, i = 0; i < ChildrenAmount; ++i, ++qcci )
	{
		if (!(fAreChildrenFake.pElement[i])) continue;

		CSimpleTypeArray<CInstanceChoice>& dst_icl = TreeInstance.GetInstanceList(pChildrenNodeIndex[i]);

		*qcci = -1;
		dst_icl.PopBack();
	}

	return ret;
}

int and_or_tree::CPerceptronModel::ParseInternalNode(CTreeInstance& TreeInstance, int NodeIndex, int ChoiceLowLimit, int ChoiceHighLimit, bool fUseScoreThreshold) const
{
	int ret = 0;

	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return ret;

	CSimpleTypeArray<double> ShapeFeatureList;
	CSimpleTypeArray<double> AppearanceFeatureList;
	double ScoreThreshold;
	if (fUseScoreThreshold && NodeIndex<(int)NodeParsingScoreThresholdList.Size)
		ScoreThreshold = NodeParsingScoreThresholdList.pElement[NodeIndex];
	else
		ScoreThreshold = +S_MAXIMUM_SCORE;
	CExternalTools::le_myTimer_B.Start();

	_GenerateInternalNodeInstanceCandidateList(TreeInstance, NodeIndex, S_MaxChoiceCandidateAmount);

	CExternalTools::le_myTimer_B.Stop();
	CExternalTools::le_myTimer_C.Start();

	CSimpleTypeArray<CInstanceChoice>& icList = TreeInstance.GetInstanceList(NodeIndex);
	CInstanceChoice* qic;
	CInstanceChoice* ric;
	int i, j;
	int min_index;
	double min_score;

	CExternalTools::le_myTimer_C.Stop();
	CExternalTools::le_myTimer_D.Start();

	ret = 0;
	for ( qic = icList.pElement, i = 0; i < (int)icList.Size; ++i, ++qic )
	{
		if (qic->fHasFakeChildren)
		{
			if (!(this->_FillMissingTreeAndComputeScore(qic, TreeInstance, NodeIndex, true, false)))
				continue;
		}
		else
		{
			this->ComputeNodeConvertedShapeFeature(ShapeFeatureList, TreeInstance, NodeIndex, qic);
			this->ComputeNodeShapeScore(qic, TreeInstance, NodeIndex, ShapeFeatureList);
			//this->ComputeNodeConvertedFullFeature(ShapeFeatureList, AppearanceFeatureList, TreeInstance, NodeIndex, qic);
			//this->ComputeNodeTotalScore(qic, TreeInstance, NodeIndex, ShapeFeatureList, AppearanceFeatureList);
		}

		min_index = -1; min_score = +S_MAXIMUM_SCORE;
		for ( ric = icList.pElement, j = 0; j < ret; ++j, ++ric )
		{
			if (ric->tree_total_score < min_score)
			{
				min_index = j;
				min_score = ric->tree_total_score;
			}
			if (CParsedInstance::AreSameByInstance(*qic, *ric))
				break;
		}
		if (!qic->fHasFakeChildren)
		{
			if (j < ret)
				TreeInstance.RefreshNodeAppearanceScore(ric->node_appearance_score, NodeIndex, qic);
			else
			{
				this->ComputeNodeConvertedAppearanceFeature(AppearanceFeatureList, TreeInstance, NodeIndex, qic);
				this->ComputeNodeAppearanceScore(qic, TreeInstance, NodeIndex, AppearanceFeatureList);
			}
			qic->RefreshTotalScore();
		}
		if (j < ret)
		{
			if (ric->tree_total_score > qic->tree_total_score) continue;
			*ric = *qic;
		}
		else
		{
			if (qic->tree_total_score<ScoreThreshold && ret>=ChoiceLowLimit)
				continue;
			if (min_index>=0 && ret>=ChoiceHighLimit)
				icList.pElement[min_index] = *qic;
			else
			{
				*ric = *qic;
				++ret;
			}
		}
	}
	icList.Resize(ret);
	icList.ToBeTight();
	sort(icList.Begin(), icList.End(), CParsedInstance::CompareByTreeScore_Bigger);

	////////////
	//ret = 0;
	//qic = icList.pElement;
	//for ( i = 0; i < (int)icList.Size; ++i, ++qic )
	//{
	//	ric = icList.pElement;
	//	for ( j = 0; j < ret; ++j, ++ric )
	//		if (CParsedInstance::AreSameByInstance(*qic, *ric))
	//			break;
	//	if (j < ret) continue;
	//	*ric = *qic; ++ret;
	//}
	//icList.Resize(ret);
	//icList.ToBeTight();

	//CExternalTools::le_myTimer_C.Stop();
	//CExternalTools::le_myTimer_D.Start();

	//for ( qic = icList.pElement, i = 0; i < ret; ++i, ++qic )
	//{
	//	if (qic->fHasFakeChildren)
	//		this->_FillMissingTreeAndComputeScore(qic, TreeInstance, NodeIndex, true, false);
	//	else
	//	{
	//		this->ComputeNodeConvertedFullFeature(ShapeFeatureList, AppearanceFeatureList, TreeInstance, NodeIndex, qic);
	//		this->ComputeNodeTotalScore(qic, TreeInstance, NodeIndex, ShapeFeatureList, AppearanceFeatureList);
	//	}
	//}
	//sort(icList.Begin(), icList.End(), CParsedInstance::CompareByTreeScore_Bigger);
	//qic = icList.pElement;

	//for ( i = 0; i < ret; ++i, ++qic )
	//{
	//	if (qic->tree_total_score < ScoreThreshold)
	//		break;
	//}
	//if (i < ChoiceLowLimit)
	//	if (ret < ChoiceLowLimit)
	//		icList.Resize(ret);
	//	else
	//		icList.Resize(ChoiceLowLimit);
	//else
	//	icList.Resize(i);
	//if ((int)icList.Size > ChoiceHighLimit) icList.Resize(ChoiceHighLimit);
	////////////

	for ( qic = icList.pElement, i = 0; i < (int)icList.Size; ++i, ++qic )
		if (qic->fHasFakeChildren)
			this->_FillMissingTreeAndComputeScore(qic, TreeInstance, NodeIndex, true, true);

	CExternalTools::le_myTimer_D.Stop();

	return ret;
}

void and_or_tree::CPerceptronModel::ParseAllLeafNodes(CTreeInstance& TreeInstance, int ChoiceLowLimit, int ChoiceHighLimit, bool fUseScoreThreshold) const
{
	int i, j, o, txy;
	const CAbstractNode* qNode;
	CInstanceChoice tic;

	CSimpleTypeArray<double> OriginalShapeFeatureList;
	CSimpleTypeArray<double> OriginalAppearanceFeatureList;
	CSimpleTypeArray<double> ConvertedShapeFeatureList;
	CSimpleTypeArray<double> ConvertedAppearanceFeatureList;

	//double tic_ori;

	for ( j = 0; j < (int)TreeInstance.CandidatePixelList.Size; ++j )
	{
		txy = TreeInstance.CandidatePixelList.pElement[j];
		for ( i = 0; i < pTree->NodeAmount; ++i )
		{
			qNode = pTree->GetNode(i);
			CSimpleTypeArray<CInstanceChoice>& CurrentChoiceList = TreeInstance.GetInstanceList(i);
			if (qNode->GetChildrenAmount() > 0) continue;

			this->ExtractLeafNodeInstanceChoice(&tic, TreeInstance, i, txy);

			TreeInstance.ComputeNodeFullFeature(OriginalShapeFeatureList, OriginalAppearanceFeatureList, i, &tic);

			ConvertedShapeFeatureList.Clear(); ConvertedShapeFeatureList.PushBack(OriginalShapeFeatureList);
			this->ConvertShapeFeature(ConvertedShapeFeatureList, i);

			ConvertedAppearanceFeatureList.Clear(); ConvertedAppearanceFeatureList.PushBack(OriginalAppearanceFeatureList);
			this->ConvertAppearanceFeature(ConvertedAppearanceFeatureList, i);

			this->ComputeNodeTotalScore(&tic, TreeInstance, i, ConvertedShapeFeatureList, ConvertedAppearanceFeatureList);

			CurrentChoiceList.PushBack(tic);
		}

		if (j==(int)TreeInstance.CandidatePixelList.Size-1 || CMyRandom::NextInt((int)TreeInstance.CandidatePixelList.Size/100)==0)
		{
			CExternalTools::ConsoleTools::ClearCurrentLine();
			cout<<"Leaf "<<(j+1)<<" / "<<(int)TreeInstance.CandidatePixelList.Size;
		}
	}

	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		qNode = pTree->GetNode(i);
		CSimpleTypeArray<CInstanceChoice>& CurrentChoiceList = TreeInstance.GetInstanceList(i);
		if (qNode->GetChildrenAmount() > 0) continue;

		sort(CurrentChoiceList.Begin(), CurrentChoiceList.End(), CParsedInstance::CompareByTreeScore_Bigger);

		double ScoreThreshold;
		if (fUseScoreThreshold && i<(int)NodeParsingScoreThresholdList.Size)
			ScoreThreshold = NodeParsingScoreThresholdList.pElement[i];
		else
			ScoreThreshold = +S_MAXIMUM_SCORE;
		
		CInstanceChoice* qic;
		CInstanceChoice* ric;
		int tLen = 0;
		for ( qic = CurrentChoiceList.pElement, j = 0; j < (int)CurrentChoiceList.Size; ++j, ++qic )
		{
			if (qic->tree_total_score<ScoreThreshold && tLen>=ChoiceLowLimit) break;

			for ( ric = CurrentChoiceList.pElement, o = 0; o < tLen; ++o, ++ric )
				if (CParsedInstance::AreSameByInstance(*qic, *ric))
					break;
			if (o < tLen) continue;

			*ric = *qic;
			if (++tLen >= ChoiceHighLimit) break;
		}

		CurrentChoiceList.Resize(tLen);
		CurrentChoiceList.ToBeTight();
	}
}

void and_or_tree::CPerceptronModel::ParseTree(CTreeInstance& TreeInstance, bool fUseScoreThreshold) const
{
	int CHOICE_LOW_LIMIT = S_ChoiceLowLimit;
	int CHOICE_HIGH_LIMIT = S_ChoiceHighLimit;

	int i, candidate_amount;
	const CAbstractNode* qNode;

	TreeInstance.ClearAllInstances();
	//TreeInstance.GenerateCandidatePixelList();

	CExternalTools::le_myTimer_A.Start();

	this->ParseAllLeafNodes(TreeInstance, CHOICE_LOW_LIMIT, CHOICE_HIGH_LIMIT, fUseScoreThreshold);

	CExternalTools::le_myTimer_A.Stop();

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		qNode = pTree->GetNode(i);
		CSimpleTypeArray<CInstanceChoice>& CurrentChoiceList = TreeInstance.GetInstanceList(i);

		if (qNode->GetChildrenAmount() > 0)
			candidate_amount = this->ParseInternalNode(TreeInstance, i, CHOICE_LOW_LIMIT, CHOICE_HIGH_LIMIT, fUseScoreThreshold);
		else
			candidate_amount = (int)TreeInstance.CandidatePixelList.Size;

		CExternalTools::ConsoleTools::ClearCurrentLine();
		cout<<i<<" / "<<pTree->NodeAmount<<": "<<(int)CurrentChoiceList.Size<<" / "<<candidate_amount;
	}
}

void and_or_tree::CPerceptronModel::ParseTree(const CImg<int> imgSrcRGB, CTreeInstance& TreeInstance, bool fUseScoreThreshold) const
{
	TreeInstance.InitTree(pTree);
	TreeInstance.InitImage(imgSrcRGB);

	ParseTree(TreeInstance, fUseScoreThreshold);
}

void and_or_tree::CPerceptronModel::ParseTree(string strFN_Image, CTreeInstance& TreeInstance, bool fUseScoreThreshold) const
{
	CImg<int> imgSrcRGB;

	CExternalTools::myLoadImage(strFN_Image, imgSrcRGB);
	ParseTree(imgSrcRGB, TreeInstance, fUseScoreThreshold);
}

void and_or_tree::CPerceptronModel::ParseGroundTruth(string strFN_GroundTruth, CTreeInstance& TreeInstance) const
{
	TreeInstance.InitTree(pTree);
	TreeInstance.InitParsedImage(strFN_GroundTruth);

	int i;
	const CAbstractNode* qNode;
	double t_x, t_y;

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		qNode = pTree->GetNode(i);
		CParsedInstance* qpi = TreeInstance.ParsedInstanceList.pElement + i;

		if (qNode->GetType() == CAbstractNode::TYPE_AND)
		{
			t_x = qpi->x;
			t_y = qpi->y;
			this->ExtractInternalNodeParsedInstance(TreeInstance, i);
			qpi->x = t_x;
			qpi->y = t_y;
		}
		else if (qNode->GetType() == CAbstractNode::TYPE_LEAF)
		{
			this->ExtractLeafNodeParsedInstance(qpi, TreeInstance, i, qpi->x, qpi->y);
		}
		else
		{
			// NO USE
		}
	}

	this->ComputeTreeParsedInstanceTotalScore(TreeInstance);
}

void and_or_tree::CPerceptronModel::InitTree(const CAbstractTree* pFixedAndOrTree)
{
	int i;

	this->pTree = pFixedAndOrTree;

	CoefficientOffsetList_Shape.Resize(pTree->NodeAmount);
	CoefficientOffsetList_Appearance.Resize(pTree->NodeAmount);
	CoefficientAmount_Shape = 0;
	CoefficientAmount_Appearance = 0;
	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		const CAbstractNode* pNode = pTree->GetNode(i);
		CoefficientOffsetList_Shape.pElement[i] = CoefficientAmount_Shape;
		CoefficientOffsetList_Appearance.pElement[i] = CoefficientAmount_Appearance;
		CoefficientAmount_Shape += pNode->GetShapeFeatureDimension();
		CoefficientAmount_Appearance += pNode->GetAppearanceFeatureDimension();
	}
	CoefficientList_Shape.Resize(CoefficientAmount_Shape);
	CoefficientList_Shape.Fill(0.0);
	CoefficientList_Appearance.Resize(CoefficientAmount_Appearance);
	CoefficientList_Appearance.Fill(0.0);

	this->ScoreScaler = 1.0;

	FeatureOffsetList_Shape.Resize(pTree->NodeAmount);
	FeatureDimension_Shape = 0;
	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		const CAbstractNode* pNode = pTree->GetNode(i);
		FeatureOffsetList_Shape.pElement[i] = FeatureDimension_Shape;
		FeatureDimension_Shape += pNode->GetShapeFeatureDimension();
	}
	FeatureMeanList_Shape.Resize(FeatureDimension_Shape);
	FeatureMeanList_Shape.Fill(0.0);
	FeatureVarianceList_Shape.Resize(FeatureDimension_Shape);
	FeatureVarianceList_Shape.Fill(0.0);

	FeatureOffsetList_Appearance.Resize(pTree->NodeAmount);
	FeatureDimension_Appearance = 0;
	for ( i = 0; i < pTree->NodeAmount; ++i )
	{
		const CAbstractNode* pNode = pTree->GetNode(i);
		FeatureOffsetList_Appearance.pElement[i] = FeatureDimension_Appearance;
		FeatureDimension_Appearance += pNode->GetAppearanceFeatureDimension();
	}
	CSimpleTypeArray<double> tmpBinValueList; tmpBinValueList.Resize(1); tmpBinValueList.Fill(1.0);
	FeatureBinValueList_Appearance.Resize(FeatureDimension_Appearance);
	FeatureBinValueList_Appearance.Fill(tmpBinValueList);
	CHistogramConfig tmpHistogramConfig;
	tmpHistogramConfig.myInit((int)tmpBinValueList.Size, false, 0.0, 0.0);
	FeatureHistogramConfigList_Appearance.Resize(FeatureDimension_Appearance);
	FeatureHistogramConfigList_Appearance.Fill(tmpHistogramConfig);

	StandardNodeInstanceList.Resize(pTree->NodeAmount);
	CRichInstance TempRichInstance;
	TempRichInstance.Clear();
	StandardNodeInstanceList.Fill(TempRichInstance);

	StandardLeafNodeOrientationList.Resize(pTree->NodeAmount);
	StandardLeafNodeOrientationList.Fill(0.0);

	NodeParsingScoreThresholdList.Resize(pTree->NodeAmount);
	NodeParsingScoreThresholdList.Fill(0.0);
}

void and_or_tree::CPerceptronModel::_BuildStandardNodeInstanceList()
{
	int i, j, node_type;
	const CAbstractNode* pNode;
	int ChildrenAmount;
	const int* qci;
	CSimpleTypeArray<const CBasicInstance*> TempBiptrList;

	for ( i = pTree->NodeAmount-1; i >= 0; --i )
	{
		pNode = pTree->GetNode(i);
		ChildrenAmount = pNode->GetChildrenAmount();
		qci = pNode->GetPtrChildrenIndex();
		node_type = pNode->GetType();

		if (node_type == CAbstractNode::TYPE_AND)
		{
			TempBiptrList.Clear();
			for ( j = 0; j < ChildrenAmount; ++j, ++qci )
				TempBiptrList.PushBack(StandardNodeInstanceList.pElement + *qci);
			StandardNodeInstanceList.pElement[i].InitAll(TempBiptrList);
		}
		else if (node_type == CAbstractNode::TYPE_LEAF)
		{
			StandardNodeInstanceList.pElement[i].SetChildrenAmount(0);
		}
		else
		{
			// NO USE
		}
	}
}

void and_or_tree::CPerceptronModel::InitStandardModel(const CFileNameList& TrainPosFileList)
{
	int PosFileAmount = TrainPosFileList.FileAmount;
	CTreeInstance TreeInstance;
	int i, j, ii;
	const CAbstractNode* pNode;
	const CParsedInstance* qpi;
	char strTemp[10001];


	StandardNodeInstanceList.Resize(pTree->NodeAmount);
	CRichInstance TempRichInstance;
	TempRichInstance.Clear();
	StandardNodeInstanceList.Fill(TempRichInstance);

	StandardLeafNodeOrientationList.Resize(pTree->NodeAmount);
	StandardLeafNodeOrientationList.Fill(0.0);

	CSimpleTypeArray<CSimpleTypeArray<double>> mat_gt_leaf_ori;
	mat_gt_leaf_ori.Resize(pTree->NodeAmount);
	CSimpleTypeArray<double> TempDoubleList;
	TempDoubleList.Clear();
	mat_gt_leaf_ori.Fill(TempDoubleList);

	TreeInstance.InitTree(pTree);

	for ( i = 0; i < PosFileAmount; ++i )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		sprintf_s(strTemp, "Computing Std Model"); cout<<strTemp;
		sprintf_s(strTemp, ": Training Pos Image %d / %d", i+1, (int)PosFileAmount);
		cout<<strTemp;

		TreeInstance.InitParsedImage(TrainPosFileList.FullFileName(i));
		TreeInstance.SetMe_AT_CAbstractTree();

		for ( qpi = TreeInstance.ParsedInstanceList.pElement, j = 0; j < pTree->NodeAmount; ++j, ++qpi )
		{
			pNode = pTree->GetNode(j);

			if (pNode->GetType() != CAbstractNode::TYPE_LEAF) continue;

			StandardNodeInstanceList.pElement[j].x += qpi->x;
			StandardNodeInstanceList.pElement[j].y += qpi->y;
			mat_gt_leaf_ori.pElement[j].PushBack(qpi->rotation);
		}
	}
	for ( j = 0; j < pTree->NodeAmount; ++j )
	{
		pNode = pTree->GetNode(j);

		if (pNode->GetType() != CAbstractNode::TYPE_LEAF) continue;

		StandardNodeInstanceList.pElement[j].x /= PosFileAmount;
		StandardNodeInstanceList.pElement[j].y /= PosFileAmount;

		const double* qo = mat_gt_leaf_ori.pElement[j].pElement;
		double base = *qo;
		double tb, tt, tbb;
		for ( ii = 0; ii < 10; ++ii )
		{
			tb = tbb = 0.0;
			for ( i = 0; i < (int)PosFileAmount; ++i )
			{
				tt = CMyMath::FormatAngle_Small(qo[i] - base);
				tb += tt;
				tbb += CMyMath::sqr(tt);
			}
			tb /= PosFileAmount; tbb = sqrt(tbb / PosFileAmount);
			base = CMyMath::FormatAngle_Small(base + tb);
		}

		StandardLeafNodeOrientationList.pElement[j] = base;
	}

	_BuildStandardNodeInstanceList();

	cout<<endl;
}

void and_or_tree::CPerceptronModel::PrecomputePosFileOriginalFeature(
	const CFileNameList& TrainPosFileList,
	string strPN_TrainPosFeature
	)
{
	char strTemp[10001];

	sprintf_s(strTemp, "md %s > nul", strPN_TrainPosFeature.c_str());
	CExternalTools::ConsoleTools::RunDosCommand("", strTemp);

	int PosFileAmount = TrainPosFileList.FileAmount;

	CTreeInstance PosTreeInstance;
	int i_p;

	CSimpleTypeArray<double> PosTreeOriginalShapeFeatureList;
	CSimpleTypeArray<double> PosTreeOriginalAppearanceFeatureList;

	for ( i_p = 0; i_p < PosFileAmount; ++i_p )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		sprintf_s(strTemp, "Pre-computing Original Feature of Pos Image %d / %d...", i_p+1, PosFileAmount);
		cout<<strTemp;

		this->ParseGroundTruth(TrainPosFileList.FullFileName(i_p), PosTreeInstance);
		PosTreeInstance.ComputeTreeParsedInstanceFullFeature(PosTreeOriginalShapeFeatureList, PosTreeOriginalAppearanceFeatureList);
		MakePosShapeFeatureFileName(strTemp, strPN_TrainPosFeature.c_str(), i_p);
		CDataStructureTools::SaveSimpleTypeArray(strTemp, PosTreeOriginalShapeFeatureList);
		MakePosAppearanceFeatureFileName(strTemp, strPN_TrainPosFeature.c_str(), i_p);
		CDataStructureTools::SaveSimpleTypeArray(strTemp, PosTreeOriginalAppearanceFeatureList);
	}
	cout<<endl;
}

void and_or_tree::CPerceptronModel::InitConvertingFunctions(const CFileNameList& TrainPosFileList, const CFileNameList& TrainNegFileList)
{
	int BIN_AMOUNT = S_AppearanceFeatureBinAmount;

	int PosFileAmount = TrainPosFileList.FileAmount;
	int NegFileAmount = TrainNegFileList.FileAmount;
	CTreeInstance TreeInstance;
	int i, j, o, q, ii;
	//int oo, o1, o2;
	double ov;
	int af_offset, af_dim;
	int sf_offset, sf_dim;
	const CAbstractNode* pNode;
	char strTemp[10001];

	CSimpleTypeArray<const CParsedInstance*> TempPiptrList;
	//const int* qi;

	const CHistogramConfig* qHistogramConfig;
	CSimpleTypeArray<double>* pBinValueList;
	int TotalNegPixelAmount;

	CSimpleTypeArray<double> fv_list;
	double* afv_min;
	double* afv_max;
	CSimpleTypeArray<CSimpleTypeArray<double>> Temp_afv_BinValueList;
	double* pda;
	double* pdb;
	const double* qd;

	CParsedInstance TempParsedInstance;

	FeatureMeanList_Shape.Resize(FeatureDimension_Shape);
	FeatureMeanList_Shape.Fill(0.0);
	FeatureVarianceList_Shape.Resize(FeatureDimension_Shape);
	FeatureVarianceList_Shape.Fill(0.0);

	TreeInstance.InitTree(pTree);

	afv_min = new double[FeatureDimension_Appearance];
	afv_max = new double[FeatureDimension_Appearance];
	Temp_afv_BinValueList.Resize(FeatureDimension_Appearance);
	for ( o = 0; o < FeatureDimension_Appearance; ++o )
	{
		afv_min[o] = +S_MAXIMUM_SCORE;
		afv_max[o] = -S_MAXIMUM_SCORE;
	}

	CSimpleTypeArray<double> mat_gt_sf;
	mat_gt_sf.Resize(((size_t)PosFileAmount) * (size_t)FeatureDimension_Shape);
	double* p_gt_sf;

	for ( i = 0; i < PosFileAmount; ++i )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		sprintf_s(strTemp, "Computing AF range [Pos] & SF m/v"); cout<<strTemp;
		sprintf_s(strTemp, ": Training Pos Image %d / %d", i+1, (int)PosFileAmount);
		cout<<strTemp;

		this->ParseGroundTruth(TrainPosFileList.FullFileName(i), TreeInstance);
		//TreeInstance.InitParsedImage(TrainPosFileList.FullFileName(i));
		TreeInstance.SetMe_AT_CAbstractTree();

		for ( j = 0; j < pTree->NodeAmount; ++j )
		{
			pNode = pTree->GetNode(j);

			const CParsedInstance* qpi = TreeInstance.ParsedInstanceList.pElement + j;

			af_offset = FeatureOffsetList_Appearance.pElement[j];
			af_dim = pNode->GetAppearanceFeatureDimension();

			pNode->ComputeAppearanceFeature(fv_list, qpi);

			pda = afv_min + af_offset;
			pdb = afv_max + af_offset;
			qd = fv_list.pElement;
			for ( o = 0; o < af_dim; ++o, ++pda, ++pdb, ++qd )
			{
				if (*qd<*pda) *pda = *qd;
				if (*qd>*pdb) *pdb = *qd;
			}

			sf_offset = FeatureOffsetList_Shape.pElement[j];
			sf_dim = pNode->GetShapeFeatureDimension();

			TreeInstance.ExtractParsedInstancePtrList(TempPiptrList, j);
			pNode->ComputeShapeFeature(fv_list, qpi, TempPiptrList);

			p_gt_sf = mat_gt_sf.pElement + i + PosFileAmount * sf_offset;
			qd = fv_list.pElement;
			for ( o = 0; o < sf_dim; ++o, ++qd )
			{
				*p_gt_sf = *qd;
				p_gt_sf += PosFileAmount;
			}
		}
	}

	for ( j = 0; j < pTree->NodeAmount; ++j )
	{
		double tb, tt;

		pNode = pTree->GetNode(j);
		sf_offset = FeatureOffsetList_Shape.pElement[j];
		sf_dim = pNode->GetShapeFeatureDimension();
		for ( o = 0; o < sf_dim; ++o, ++qd )
		{
			p_gt_sf = mat_gt_sf.pElement + PosFileAmount * (sf_offset + o);

			pda = FeatureMeanList_Shape.pElement + (sf_offset + o);
			*pda = *p_gt_sf;
			for ( ii = 0; ii < 10; ++ii )
			{
				tb = 0.0;
				for ( i = 0; i < PosFileAmount; ++i )
				{
					tt = p_gt_sf[i] - *pda;
					if (o < 6) tt = CMyMath::FormatAngle(tt);
					tb += tt;
				}
				*pda = *pda + tb / PosFileAmount;
				if (o < 6) *pda = CMyMath::FormatAngle(*pda);
			}

			pdb = FeatureVarianceList_Shape.pElement + (sf_offset + o);
			*pdb = 0.0;
			if (o < 6)
				for ( i = 0; i < PosFileAmount; ++i ) *pdb += CMyMath::sqr(CMyMath::FormatAngle(p_gt_sf[i] - *pda));
			else
				for ( i = 0; i < PosFileAmount; ++i ) *pdb += CMyMath::sqr(p_gt_sf[i] - *pda);
			*pdb = sqrt(*pdb / PosFileAmount);
		}
	}

	cout<<endl; // Shape Feature Done

	for ( i = 0; i < PosFileAmount + NegFileAmount; ++i )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		if (i < PosFileAmount)
			sprintf_s(strTemp, "Computing AF range [Neg]: Training Pos Image %d / %d", i+1, (int)PosFileAmount);
		else
			sprintf_s(strTemp, "Computing AF range [Neg]: Processing Training Neg Image %d / %d", i+1-(int)PosFileAmount, (int)NegFileAmount);
		cout<<strTemp;
		if (i == PosFileAmount-1) cout<<endl;

		if (i < PosFileAmount)
			TreeInstance.InitParsedImage(TrainPosFileList.FullFileName(i));
		else
			TreeInstance.InitImage(TrainNegFileList.FullFileName(i - PosFileAmount));
		//TreeInstance.GenerateCandidatePixelList();
		TreeInstance.SetMe_AT_CAbstractTree();

		for ( j = 0; j < pTree->NodeAmount; ++j )
		{
			pNode = pTree->GetNode(j);

			af_offset = FeatureOffsetList_Appearance.pElement[j];
			af_dim = pNode->GetAppearanceFeatureDimension();

			for ( q = 0; q < (int)TreeInstance.CandidatePixelList.Size; ++q )
			{
				CParsedInstance tpi;
				o = TreeInstance.CandidatePixelList.pElement[q];
				this->ExtractLeafNodeParsedInstance(&tpi, TreeInstance, j, o);

				pNode->ComputeAppearanceFeature(fv_list, &tpi);
				pda = afv_min + af_offset;
				pdb = afv_max + af_offset;
				qd = fv_list.pElement;
				for ( o = 0; o < af_dim; ++o, ++pda, ++pdb, ++qd )
				{
					if (*qd<*pda) *pda = *qd;
					if (*qd>*pdb) *pdb = *qd;
				}
			}
		}
	}
	cout<<endl;

	for ( o = 0; o < FeatureDimension_Appearance; ++o )
	{
		FeatureHistogramConfigList_Appearance.pElement[o].myInit(BIN_AMOUNT, false, afv_min[o], afv_max[o]);
		FeatureBinValueList_Appearance.pElement[o].Resize(BIN_AMOUNT);
		FeatureBinValueList_Appearance.pElement[o].Fill(0.0);
		Temp_afv_BinValueList.pElement[o].Resize(BIN_AMOUNT);
		Temp_afv_BinValueList.pElement[o].Fill(0.0);
	}
	delete[] afv_min;
	delete[] afv_max;

	TotalNegPixelAmount = 0;
	for ( i = 0; i < PosFileAmount + NegFileAmount; ++i )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		if (i < PosFileAmount)
			sprintf_s(strTemp, "Computing AF hist [Neg]: Training Pos Image %d / %d", i+1, (int)PosFileAmount);
		else
			sprintf_s(strTemp, "Computing AF hist [Neg]: Processing Training Neg Image %d / %d", i+1-(int)PosFileAmount, (int)NegFileAmount);
		cout<<strTemp;
		if (i == PosFileAmount-1) cout<<endl;

		if (i < PosFileAmount)
			TreeInstance.InitParsedImage(TrainPosFileList.FullFileName(i));
		else
			TreeInstance.InitImage(TrainNegFileList.FullFileName(i - PosFileAmount));
		//TreeInstance.GenerateCandidatePixelList();
		TreeInstance.SetMe_AT_CAbstractTree();

		TotalNegPixelAmount += (int)TreeInstance.CandidatePixelList.Size;

		for ( j = 0; j < pTree->NodeAmount; ++j )
		{
			pNode = pTree->GetNode(j);

			af_offset = FeatureOffsetList_Appearance.pElement[j];
			af_dim = pNode->GetAppearanceFeatureDimension();
			for ( q = 0; q < (int)TreeInstance.CandidatePixelList.Size; ++q )
			{
				CParsedInstance tpi;
				o = TreeInstance.CandidatePixelList.pElement[q];
				this->ExtractLeafNodeParsedInstance(&tpi, TreeInstance, j, o);

				pNode->ComputeAppearanceFeature(fv_list, &tpi);

				qHistogramConfig = FeatureHistogramConfigList_Appearance.pElement + af_offset;
				pBinValueList = FeatureBinValueList_Appearance.pElement + af_offset;
				qd = fv_list.pElement;
				for ( o = 0; o < af_dim; ++o, ++qHistogramConfig, ++pBinValueList, ++qd )
				{
					pBinValueList->pElement[qHistogramConfig->SelectBin_Nearest(*qd)] += 1.0;
					//ov = qHistogramConfig->SelectBin_Bounding(o1, o2, *qd);
					//if (ov<-EPS || ov>1+EPS)
					//	ov = EPS;
					//pBinValueList->pElement[o1] += 1.0 * (1-ov);
					//pBinValueList->pElement[o2] += 1.0 * ov;
				}
			}
		}
	}
	ov = TotalNegPixelAmount;
	//ov = log(TotalNegPixelAmount + 0.0);
	for ( o = 0; o < FeatureDimension_Appearance; ++o )
	{
		pda = FeatureBinValueList_Appearance.pElement[o].pElement;
		pdb = Temp_afv_BinValueList.pElement[o].pElement;
		for ( q = 0; q < BIN_AMOUNT; ++q, ++pda, ++pdb )
		{
			*pdb = *pda/ov;
			*pda = 0.0;
			//if (*pda < EPS) *pda = EPS;
			//*pdb = log(*pda) - ov;
			//*pda = 0.0;
		}
	}
	cout<<endl;

	for ( i = 0; i < PosFileAmount; ++i )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		sprintf_s(strTemp, "Computing AF hist [Pos]: Training Pos Image %d / %d", i+1, (int)PosFileAmount);
		cout<<strTemp;

		this->ParseGroundTruth(TrainPosFileList.FullFileName(i), TreeInstance);
		//TreeInstance.InitParsedImage(TrainPosFileList.FullFileName(i));
		TreeInstance.SetMe_AT_CAbstractTree();

		for ( j = 0; j < pTree->NodeAmount; ++j )
		{
			pNode = pTree->GetNode(j);

			const CParsedInstance* qpi = TreeInstance.ParsedInstanceList.pElement + j;

			af_offset = FeatureOffsetList_Appearance.pElement[j];
			af_dim = pNode->GetAppearanceFeatureDimension();

			pNode->ComputeAppearanceFeature(fv_list, qpi);

			qHistogramConfig = FeatureHistogramConfigList_Appearance.pElement + af_offset;
			pBinValueList = FeatureBinValueList_Appearance.pElement + af_offset;
			qd = fv_list.pElement;
			for ( o = 0; o < af_dim; ++o, ++qHistogramConfig, ++pBinValueList, ++qd )
			{
				pBinValueList->pElement[qHistogramConfig->SelectBin_Nearest(*qd)] += 1.0;
				//ov = qHistogramConfig->SelectBin_Bounding(o1, o2, *qd);
				//if (ov<-EPS || ov>1+EPS)
				//	ov = EPS;
				//pBinValueList->pElement[o1] += 1.0 * (1-ov);
				//pBinValueList->pElement[o2] += 1.0 * ov;
			}
		}
	}
	ov = PosFileAmount;
	//ov = log(PosFileAmount + 0.0);
	for ( o = 0; o < FeatureDimension_Appearance; ++o )
	{
		pda = FeatureBinValueList_Appearance.pElement[o].pElement;
		pdb = Temp_afv_BinValueList.pElement[o].pElement;
		for ( q = 0; q < BIN_AMOUNT; ++q, ++pda, ++pdb )
		{
			*pda = *pda/ov - *pdb;
			//if (*pda < EPS) *pda = EPS;
			//*pda = log(*pda) - ov - *pdb;
		}
	}
	cout<<endl;
}

void and_or_tree::CPerceptronModel::ComputeNodeScoreThresholdForParsing(int TrainPosFileAmount, string strPN_TrainPosFeature, double TargetRecall)
{
	int target_j = (int)(TrainPosFileAmount * (1.0 - TargetRecall) + 0.5) - 1;

	int i_p, j;
	double* mat_score;
	double* pScore;
	char strTemp[10001];

	CSimpleTypeArray<double> PosTreeScoreList;
	CSimpleTypeArray<double> PosTreeConvertedShapeFeatureList;
	CSimpleTypeArray<double> PosTreeConvertedAppearanceFeatureList;
	CSimpleTypeArray<double> PosTreeOriginalShapeFeatureList;
	CSimpleTypeArray<double> PosTreeOriginalAppearanceFeatureList;

	NodeParsingScoreThresholdList.Resize(pTree->NodeAmount);
	NodeParsingScoreThresholdList.Fill(-S_MAXIMUM_SCORE);

	CExternalTools::ConsoleTools::ClearCurrentLine();
	sprintf_s(strTemp, "Computing Score Threshold...");
	cout<<strTemp;

	mat_score = new double [((size_t)pTree->NodeAmount) * (size_t)TrainPosFileAmount];
	for ( i_p = 0; i_p < TrainPosFileAmount; ++i_p )
	{
		//CExternalTools::ConsoleTools::ClearCurrentLine();
		//sprintf_s(strTemp, "Computing Score Threshold: Training Pos Image %d / %d", i_p+1, TrainPosFileAmount);
		//cout<<strTemp;

		MakePosShapeFeatureFileName(strTemp, strPN_TrainPosFeature.c_str(), i_p);
		CDataStructureTools::LoadSimpleTypeArray(PosTreeOriginalShapeFeatureList, strTemp);
		MakePosAppearanceFeatureFileName(strTemp, strPN_TrainPosFeature.c_str(), i_p);
		CDataStructureTools::LoadSimpleTypeArray(PosTreeOriginalAppearanceFeatureList, strTemp);

		this->ConvertTreeFeature_SingleInstance(
			PosTreeConvertedShapeFeatureList,
			PosTreeConvertedAppearanceFeatureList,
			PosTreeOriginalShapeFeatureList,
			PosTreeOriginalAppearanceFeatureList
			);
		this->ComputeTreeTotalScore_ByFeature(
			PosTreeScoreList,
			PosTreeConvertedShapeFeatureList,
			PosTreeConvertedAppearanceFeatureList
			);

		for ( j = 0; j < pTree->NodeAmount; ++j )
			mat_score[i_p + j * TrainPosFileAmount] = PosTreeScoreList.pElement[j];
	}

	CExternalTools::ConsoleTools::ClearCurrentLine();
	sprintf_s(strTemp, "Finished Computing Score Threshold...");
	cout<<strTemp<<endl;

	for ( j = 0; j < pTree->NodeAmount; ++j )
	{
		pScore = mat_score + j * TrainPosFileAmount;
		sort(pScore, pScore + TrainPosFileAmount);
		if (target_j < 0)
			NodeParsingScoreThresholdList.pElement[j] = pScore[0] - EPS;
		else if (target_j+1 < TrainPosFileAmount)
			NodeParsingScoreThresholdList.pElement[j] = (pScore[target_j] + pScore[target_j+1]) * 0.5;
		else
			NodeParsingScoreThresholdList.pElement[j] = pScore[TrainPosFileAmount-1] + EPS;
	}
	delete[] mat_score;
}

void and_or_tree::CPerceptronModel::ComputeNodeScoreThresholdForParsing(const CFileNameList& TrainPosFileList, double TargetRecall)
{
	int TrainPosFileAmount = TrainPosFileList.FileAmount;
	int target_j = (int)(TrainPosFileAmount * (1.0 - TargetRecall) + 0.5) - 1;

	CTreeInstance TreeInstance;
	int i, j;
	double* mat_score;
	double* pScore;
	char strTemp[10001];

	NodeParsingScoreThresholdList.Resize(pTree->NodeAmount);

	TreeInstance.InitTree(pTree);
	mat_score = new double [((size_t)pTree->NodeAmount) * (size_t)TrainPosFileAmount];
	for ( i = 0; i < TrainPosFileAmount; ++i )
	{
		CExternalTools::ConsoleTools::ClearCurrentLine();
		sprintf_s(strTemp, "Computing Score Threshold: Training Pos Image %d / %d", i+1, TrainPosFileAmount);
		cout<<strTemp;

		this->ParseGroundTruth(TrainPosFileList.FullFileName(i), TreeInstance);

		for ( j = 0; j < pTree->NodeAmount; ++j )
			mat_score[i + j * TrainPosFileAmount] = TreeInstance.ParsedInstanceList.pElement[j].tree_total_score;
	}

	CExternalTools::ConsoleTools::ClearCurrentLine();
	sprintf_s(strTemp, "Finished Computing Score Threshold...");
	cout<<strTemp;

	for ( j = 0; j < pTree->NodeAmount; ++j )
	{
		pScore = mat_score + j * TrainPosFileAmount;
		sort(pScore, pScore + TrainPosFileAmount);
		if (target_j < 0)
			NodeParsingScoreThresholdList.pElement[j] = pScore[0] - EPS;
		else if (target_j+1 < TrainPosFileAmount)
			NodeParsingScoreThresholdList.pElement[j] = (pScore[target_j] + pScore[target_j+1]) * 0.5;
		else
			NodeParsingScoreThresholdList.pElement[j] = pScore[TrainPosFileAmount-1] + EPS;
	}
	delete[] mat_score;
}

void and_or_tree::CPerceptronModel::UpdateModel(CSimpleTypeArray<double>& DeltaCoefficientList, double Alpha)
{
	int o;
	double* pd;
	const double* qd;

	//CMyMath::L2_Normalize(DeltaCoefficientList.pElement, DeltaCoefficientList.Size, (double)EPS);

	//double tt = CMyMath::Compute_L2_Norm(DeltaCoefficientList.pElement, DeltaCoefficientList.Size, (double)EPS);
	//CExternalTools::ConsoleTools::ClearCurrentLine();
	//cout<<setprecision(6)<<fixed<<tt<<endl;

	pd = CoefficientList_Shape.pElement; qd = DeltaCoefficientList.pElement;
	for ( o = 0; o < CoefficientAmount_Shape; ++o, ++pd, ++qd ) *pd += *qd * Alpha;
	pd = CoefficientList_Appearance.pElement; qd = DeltaCoefficientList.pElement + CoefficientAmount_Shape;
	for ( o = 0; o < CoefficientAmount_Appearance; ++o, ++pd, ++qd ) *pd += *qd * Alpha;
	DeltaCoefficientList.Fill(0.0);
	NormalizeModel(false);
}

void and_or_tree::CPerceptronModel::NormalizeModel(bool fNormalizeScoreScaler)
{
	size_t t1 = CoefficientList_Shape.Size;
	size_t t2 = CoefficientList_Appearance.Size;
	size_t i;
	double* pd;

	CSimpleTypeArray<double> TempList;
	TempList.Clear();
	TempList.PushBack(CoefficientList_Shape);
	TempList.PushBack(CoefficientList_Appearance);

	//double tt = CMyMath::Compute_L2_Norm(TempList.pElement, TempList.Size, (double)EPS);
	//CExternalTools::ConsoleTools::ClearCurrentLine();
	//cout<<setprecision(6)<<fixed<<tt<<endl;

	ScoreScaler = CMyMath::Compute_L2_Norm(TempList.pElement, t1+t2, (double)EPS);
	ScoreScaler = 1.0 / ScoreScaler;

	if (fNormalizeScoreScaler)
	{
		pd = CoefficientList_Shape.pElement;
		for ( i = 0; i < t1; ++i )
			*(pd++) *= ScoreScaler;

		pd = CoefficientList_Appearance.pElement;
		for ( i = 0; i < t2; ++i )
			*(pd++) *= ScoreScaler;

		ScoreScaler = 1.0;
	}
}

void and_or_tree::CPerceptronModel::Test(
	const CFileNameList& FileList, bool fTypePosNeg,
	string strPN_TrainPosFeature, string strPN_ParsedImage,
	ostream& oLog
	)
{
	double TARGET_RECALL = S_DefaultTargetRecall;

	int FileAmount = FileList.FileAmount;

	CTreeInstance ti_PosGroundTruth;
	CTreeInstance ti_ParseResult;

	int i_p;
	int i, j, o;
	double gt_score, parse_score;

	char strTemp[100001];
	CImg<int> imgOutput;

	CSimpleTypeArray<double> sf_list;
	CSimpleTypeArray<double> af_list;

	sprintf_s(strTemp, "md %s > nul", strPN_ParsedImage.c_str());
	CExternalTools::ConsoleTools::RunDosCommand("", strTemp);

	CExternalTools::ResetAllTimers();

	CSimpleTypeArray<double> PerNodeRecallList;
	PerNodeRecallList.Resize(pTree->NodeAmount);
	PerNodeRecallList.Fill(0.0);

	double ape = 0.0;

	//i_p = 5;
	for ( i_p = 0; i_p < FileAmount; ++i_p )
	{
		if (fTypePosNeg)
		{
			this->ParseGroundTruth(FileList.FullFileName(i_p), ti_PosGroundTruth);
			gt_score = ti_PosGroundTruth.ParsedInstanceList.pElement[0].tree_total_score;

			ti_ParseResult.CloneDataFrom(ti_PosGroundTruth, true);
			this->ParseTree(ti_ParseResult, true);
		}
		else
		{
			gt_score = 0.0;
			this->ParseTree(FileList.FullFileName(i_p), ti_ParseResult, true);
		}

		ti_ParseResult.DrawCandidatePixelList(imgOutput);
		sprintf_s(strTemp, "%sCandidate.%02d.Pos.%04d.jpg", strPN_ParsedImage.c_str(), -1, i_p);
		CExternalTools::mySaveImage(strTemp, imgOutput);

		for ( j = 0; j < pTree->NodeAmount; ++j )
		{
			ti_ParseResult.DrawWhiteImage(imgOutput);
			ti_ParseResult.DrawNodeInstanceChoiceSet(imgOutput, j, true);
			const CParsedInstance& qpi = ti_PosGroundTruth.ParsedInstanceList.pElement[j];
			ti_ParseResult.DrawPoint(imgOutput, (int)(qpi.x+EPS), (int)(qpi.y+EPS), 24, pTree->GetNodeColor(j));
			sprintf_s(strTemp, "%s%02d.Pos.%04d.Node.%02d.jpg", strPN_ParsedImage.c_str(), -1, i_p, j);
			//CExternalTools::mySaveImage(strTemp, imgOutput);
		}

		sprintf_s(strTemp, "Image %d / %d...", i_p+1, FileAmount);
		CExternalTools::ConsoleTools::ClearCurrentLine();
		cout<<strTemp<<endl;
		oLog<<strTemp<<endl;

		sprintf_s(strTemp, "Leaf / Generate / Unique / Compute: %.3lf / %.3lf / %.3lf / %.3lf mins"
			, CExternalTools::le_myTimer_A.GetCumulativeTime()/60.0
			, CExternalTools::le_myTimer_B.GetCumulativeTime()/60.0
			, CExternalTools::le_myTimer_C.GetCumulativeTime()/60.0
			, CExternalTools::le_myTimer_D.GetCumulativeTime()/60.0
			);
		oLog<<strTemp<<endl;
		oLog<<endl;

		for ( i = 0; i < S_TestPrintAmountPerImageForTest; ++i )
		{
			if (!ti_ParseResult.ParseFromTop(i)) break;

			parse_score = ti_ParseResult.ParsedInstanceList.pElement[i].tree_total_score;

			ti_ParseResult.DrawParsedImage(imgOutput, parse_score);
			sprintf_s(strTemp, "%s%02d.Pos.%04d.%02d.jpg", strPN_ParsedImage.c_str(), -1, i_p, i);
			CExternalTools::mySaveImage(strTemp, imgOutput);

			for ( j = 0; j < pTree->NodeAmount; ++j )
			{
				const CParsedInstance* qpi = NULL;
				bool fSelected, fMatch, fExistMatch;
				char sgn;

				if (i==0 && pTree->GetNode(j)->GetType()==CAbstractNode::TYPE_LEAF)
					ape += sqrt(CMyMath::sqr(
						ti_ParseResult.ParsedInstanceList.pElement[j].x - 
						ti_PosGroundTruth.ParsedInstanceList.pElement[j].x
						) + CMyMath::sqr(
						ti_ParseResult.ParsedInstanceList.pElement[j].y - 
						ti_PosGroundTruth.ParsedInstanceList.pElement[j].y
						));

				//sprintf_s(strTemp, "Image.%d.Instance.%d.Node.%d:", i_p, i, j); oLog<<strTemp<<endl;
				if (fTypePosNeg)
				{
					sprintf_s(strTemp, "  Pos    ", 0); oLog<<strTemp;
					qpi = ti_PosGroundTruth.ParsedInstanceList.pElement + j;
					qpi->OutputToStream(oLog, true);
					//sprintf_s(strTemp, "  --  %.2lf", CMyMath::FormatAngle(StandardLeafNodeOrientationList.pElement[j], PI)); oLog<<strTemp;
					oLog<<endl;
				}
				const CSimpleTypeArray<CInstanceChoice>& parse_ic_list = ti_ParseResult.GetInstanceList(j);
				fExistMatch = false;
				for ( o = 0; o < (int)parse_ic_list.Size; ++o )
				{
					fMatch = fTypePosNeg && CParsedInstance::AreSameByInstance(*qpi, parse_ic_list.pElement[o]);
					fExistMatch |= fMatch;
					fSelected = (parse_ic_list.pElement[o].Tag & 1);
					if (!fMatch && !fSelected) continue;

					sgn = ' ';
					if (fMatch && fSelected) sgn = '*';
					if (fMatch && !fSelected) sgn = 'o';

					//sprintf_s(strTemp, "%c Neg %03d", sgn, o); oLog<<strTemp;
					//parse_ic_list.pElement[o].OutputToStream(oLog, true); oLog<<endl;
				}
				if (fExistMatch && i==0) PerNodeRecallList.pElement[j] += 100.0 / FileAmount;
				//oLog<<endl;
			}

			o = i;
		}

		o = i;
		//cout<<i_p<<endl;
		//oLog<<i_p<<endl;
		//break;
	}

	ape /= FileAmount;
	sprintf_s(strTemp, "APE = %.2lf", ape);
	cout<<strTemp<<endl;
	oLog<<strTemp<<endl;

	CExternalTools::ConsoleTools::ClearCurrentLine();
	cout<<endl;
	oLog<<endl;
	for ( j = 0; j < pTree->NodeAmount; ++j )
	{
		sprintf_s(strTemp, "Node %d Recall = %.2lf", j, PerNodeRecallList.pElement[j]);
		cout<<strTemp<<endl;
		oLog<<strTemp<<endl;
	}
}


void and_or_tree::CPerceptronModel::Train(
	const CFileNameList& TrainPosFileList, const CFileNameList& TrainNegFileList,
	string strPN_TrainPosFeature, string strPN_ParsedImage, string strPN_NewModel,
	ostream& oLog
	)
{
	double TARGET_RECALL = S_DefaultTargetRecall;

	int TRAIN_ROUND_AMOUNT = S_TrainRoundAmount;
	int PER_IMAGE_NEG_AMOUNT = S_PerNegImageInstanceAmountForTrain;

	int PosFileAmount = TrainPosFileList.FileAmount;
	int NegFileAmount = TrainNegFileList.FileAmount;

	CSimpleTypeArray<double> DeltaCoefficientList;
	DeltaCoefficientList.Resize(CoefficientAmount_Shape + CoefficientAmount_Appearance);
	DeltaCoefficientList.Fill(0.0);
	double* pDeltaCoefficient_Shape = DeltaCoefficientList.pElement + 0;
	double* pDeltaCoefficient_Appearance = DeltaCoefficientList.pElement + CoefficientAmount_Shape;

	char strTemp[100001];
	CImg<int> imgOutput;

	CTreeInstance ti_PosGroundTruth;
	CTreeInstance ti_ParseResult;

	int i_round, i_itr, i_p, i_n;
	double pos_score, neg_score;
	int i;
	int update_amount_per_image;
	int update_amount_per_round;

	CSimpleTypeArray<double> sf_list;
	CSimpleTypeArray<double> af_list;

	sprintf_s(strTemp, "md %s > nul", strPN_ParsedImage.c_str());
	CExternalTools::ConsoleTools::RunDosCommand("", strTemp);
	sprintf_s(strTemp, "md %s > nul", strPN_NewModel.c_str());
	CExternalTools::ConsoleTools::RunDosCommand("", strTemp);

	CSimpleTypeArray<double> gt_score_list;
	CSimpleTypeArray<double> gt_sf_src_list;
	CSimpleTypeArray<double> gt_af_src_list;
	CSimpleTypeArray<double> gt_sf_cvt_list;
	CSimpleTypeArray<double> gt_af_cvt_list;

	CExternalTools::ResetAllTimers();

	NormalizeModel(false); ComputeNodeScoreThresholdForParsing(PosFileAmount, strPN_TrainPosFeature, TARGET_RECALL);

	for ( i_itr = i_round = 0; i_round < TRAIN_ROUND_AMOUNT; ++i_round )
	{
		update_amount_per_round = 0;

		for ( i_p = 0; i_p < PosFileAmount; ++i_p, ++i_itr )
		{
			this->ParseGroundTruth(TrainPosFileList.FullFileName(i_p), ti_PosGroundTruth);
			pos_score = ti_PosGroundTruth.ParsedInstanceList.pElement[0].tree_total_score;

			ti_ParseResult.CloneDataFrom(ti_PosGroundTruth, true);
			this->ParseTree(ti_ParseResult, true);

			update_amount_per_image = 0;
			for ( i = 0; i < PER_IMAGE_NEG_AMOUNT; ++i )
			{
				if (i >= (int)ti_ParseResult.GetInstanceList(0).Size) break;
				neg_score = ti_ParseResult.GetInstance(0, i).tree_total_score;

				if (i == 0)
				{
					ti_ParseResult.ParseFromTop(i);
					ti_ParseResult.DrawParsedImage(imgOutput, neg_score);

					sprintf_s(strTemp, "%s%02d.Pos.%04d.%02d.jpg", strPN_ParsedImage.c_str(), i_round, i_p, i);
					CExternalTools::mySaveImage(strTemp, imgOutput);
				}

				//if (i_p == 3)
				//{
				//	int j;
				//	bool fTypePosNeg = true;
				//	for ( j = 0; j < pTree->NodeAmount; ++j )
				//	{
				//		const CParsedInstance* qpi = NULL;
				//		bool fMatch, fExistMatch;

				//		sprintf_s(strTemp, "Node %d:", j); oLog<<strTemp<<endl;
				//		if (fTypePosNeg)
				//		{
				//			sprintf_s(strTemp, "  Pos    ", 0); oLog<<strTemp;
				//			qpi = ti_PosGroundTruth.ParsedInstanceList.pElement + j;
				//			qpi->OutputToStream(oLog, true); oLog<<endl;
				//		}
				//		const CSimpleTypeArray<CInstanceChoice>& parse_ic_list = ti_ParseResult.GetInstanceList(j);
				//		fExistMatch = false;
				//		for ( i = 0; i < (int)parse_ic_list.Size; ++i )
				//		{
				//			fMatch = fTypePosNeg && CParsedInstance::AreSameByInstance(*qpi, parse_ic_list.pElement[i]);
				//			fExistMatch |= fMatch;
				//			if (!fMatch && !(parse_ic_list.pElement[i].Tag & 1)) continue;

				//			sprintf_s(strTemp, "%c Neg %03d", ( fMatch? '*' : ' '), i); oLog<<strTemp;
				//			parse_ic_list.pElement[i].OutputToStream(oLog, true); oLog<<endl;
				//		}
				//		//if (fExistMatch) PerNodeRecallList.pElement[j] += 100.0 / FileAmount;
				//		oLog<<endl;
				//	}
				//	exit(0);
				//}

				//if (neg_score < pos_score) continue;

				++update_amount_per_image;
				++update_amount_per_round;

				_UpdateModel(pDeltaCoefficient_Shape, pDeltaCoefficient_Appearance, ti_PosGroundTruth, 0, +1.0);
				_UpdateModel(pDeltaCoefficient_Shape, pDeltaCoefficient_Appearance, ti_ParseResult, 0, -1.0);
			}

			sprintf_s(strTemp, "Rnd %d Pos Image %d / %d: %d / %d Updates", i_round, i_p+1, PosFileAmount, update_amount_per_image, update_amount_per_round);
			CExternalTools::ConsoleTools::ClearCurrentLine();
			cout<<strTemp<<endl;
			oLog<<strTemp<<endl;

			sprintf_s(strTemp, "Leaf / Generate / Unique / Compute: %.2lf / %.2lf / %.2lf / %.2lf mins"
				, CExternalTools::le_myTimer_A.GetCumulativeTime()/60.0
				, CExternalTools::le_myTimer_B.GetCumulativeTime()/60.0
				, CExternalTools::le_myTimer_C.GetCumulativeTime()/60.0
				, CExternalTools::le_myTimer_D.GetCumulativeTime()/60.0
				);
			oLog<<strTemp<<endl;

			//if (i_p == 003)
			//{
			//	double tt = CMyMath::Compute_L2_Norm(DeltaCoefficientList.pElement, DeltaCoefficientList.Size, (double)EPS);
			//	int jj;
			//	double zz = EPS * DeltaCoefficientList.Size;
			//	double yy;

			//	for ( jj = 0; jj < (int)DeltaCoefficientList.Size; ++jj )
			//	{
			//		yy = DeltaCoefficientList.pElement[jj];
			//		zz += CMyMath::sqr(yy);
			//		oLog<<jj<<"\t"<<setprecision(6)<<showpos<<scientific<<yy<<"\t"<<showpos<<scientific<<zz<<endl;
			//	}

			//	exit(0);
			//}

			UpdateModel(DeltaCoefficientList, 1.0);
			ComputeNodeScoreThresholdForParsing(TrainPosFileList.FileAmount, strPN_TrainPosFeature, TARGET_RECALL);

			sprintf_s(strTemp, "%s%02d.PerceptronModel.Pos.%04d.txt", strPN_NewModel.c_str(), i_round, i_p);
			this->SaveToFile(strTemp);
		}

		for ( i_n = 0; i_n < NegFileAmount; ++i_n, ++i_itr )
		{
			this->ParseTree(TrainNegFileList.FullFileName(i_n), ti_ParseResult, true);
			
			update_amount_per_image = 0;
			for ( i = 0; i < PER_IMAGE_NEG_AMOUNT; ++i )
			{
				if (i >= (int)ti_ParseResult.GetInstanceList(0).Size) break;
				neg_score = ti_ParseResult.GetInstance(0, i).tree_total_score;

				if (i == 0)
				{
					ti_ParseResult.ParseFromTop(i);
					ti_ParseResult.DrawParsedImage(imgOutput, neg_score);

					sprintf_s(strTemp, "%s%02d.Neg.%04d.%02d.jpg", strPN_ParsedImage.c_str(), i_round, i_n, i);
					CExternalTools::mySaveImage(strTemp, imgOutput);
				}

				for ( i_p = 0; i_p < PosFileAmount; ++i_p )
				{
					MakePosShapeFeatureFileName(strTemp, strPN_TrainPosFeature.c_str(), i_p);
					CDataStructureTools::LoadSimpleTypeArray(gt_sf_src_list, strTemp);
					MakePosAppearanceFeatureFileName(strTemp, strPN_TrainPosFeature.c_str(), i_p);
					CDataStructureTools::LoadSimpleTypeArray(gt_af_src_list, strTemp);

					this->ConvertTreeFeature_SingleInstance(gt_sf_cvt_list, gt_af_cvt_list, gt_sf_src_list, gt_af_src_list);
					this->ComputeTreeTotalScore_ByFeature(gt_score_list, gt_sf_cvt_list, gt_af_cvt_list);

					pos_score = gt_score_list.pElement[0];

					if (neg_score < pos_score) continue;

					++update_amount_per_image;
					++update_amount_per_round;

					_UpdateModel(pDeltaCoefficient_Shape, pDeltaCoefficient_Appearance, gt_sf_cvt_list, gt_af_cvt_list, 0, +1.0);
					_UpdateModel(pDeltaCoefficient_Shape, pDeltaCoefficient_Appearance, ti_ParseResult, 0, -1.0);
				}
			}

			sprintf_s(strTemp, "Rnd %d Neg Image %d / %d [%d]: %d / %d Updates", i_round, i_n+1, NegFileAmount, i, update_amount_per_image, update_amount_per_round);
			CExternalTools::ConsoleTools::ClearCurrentLine();
			cout<<strTemp<<endl;
			oLog<<strTemp<<endl;

			sprintf_s(strTemp, "Leaf / Generate / Unique / Compute: %.3lf / %.3lf / %.3lf / %.3lf mins"
				, CExternalTools::le_myTimer_A.GetCumulativeTime()/60.0
				, CExternalTools::le_myTimer_B.GetCumulativeTime()/60.0
				, CExternalTools::le_myTimer_C.GetCumulativeTime()/60.0
				, CExternalTools::le_myTimer_D.GetCumulativeTime()/60.0
				);
			oLog<<strTemp<<endl;

			UpdateModel(DeltaCoefficientList, 1.0);
			ComputeNodeScoreThresholdForParsing(TrainPosFileList.FileAmount, strPN_TrainPosFeature, TARGET_RECALL);

			sprintf_s(strTemp, "%s%02d.PerceptronModel.Neg.%04d.txt", strPN_NewModel.c_str(), i_round, i_n);
			this->SaveToFile(strTemp);
		}
	}
}

void and_or_tree::CPerceptronModel::_UpdateModel(
	double* pDeltaCoefficient_Shape, double* pDeltaCoefficient_Appearance,
	const CSimpleTypeArray<double>& PrecomputedShapeFeatureList,
	const CSimpleTypeArray<double>& PrecomputedAppearanceFeatureList,
	int NodeIndex, double Alpha)
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;
	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);

	int i, ii;
	double* pd;
	const double* qd;

	ii = pNode->GetShapeFeatureDimension();
	pd = pDeltaCoefficient_Shape + CoefficientOffsetList_Shape.pElement[NodeIndex];
	qd = PrecomputedShapeFeatureList.pElement + FeatureOffsetList_Shape.pElement[NodeIndex];
	for ( i = 0; i < ii; ++i, ++pd, ++qd )
		*pd += *qd * Alpha;

	ii = pNode->GetAppearanceFeatureDimension();
	pd = pDeltaCoefficient_Appearance + CoefficientOffsetList_Appearance.pElement[NodeIndex];
	qd = PrecomputedAppearanceFeatureList.pElement + FeatureOffsetList_Appearance.pElement[NodeIndex];
	for ( i = 0; i < ii; ++i, ++pd, ++qd )
		*pd += *qd * Alpha;

	ii = pNode->GetChildrenAmount();
	const int* qci = pNode->GetPtrChildrenIndex();
	for ( i = 0; i < ii; ++i )
		_UpdateModel(pDeltaCoefficient_Shape, pDeltaCoefficient_Appearance, PrecomputedShapeFeatureList, PrecomputedAppearanceFeatureList, *(qci++), Alpha);
}

void and_or_tree::CPerceptronModel::_UpdateModel(
	double* pDeltaCoefficient_Shape, double* pDeltaCoefficient_Appearance,
	const CTreeInstance& TreeInstance, int NodeIndex, double Alpha)
{
	if (NodeIndex<0 || NodeIndex>=pTree->NodeAmount) return;

	int i, ii;
	double* pd;
	const double* qd;

	this->ComputeNodeConvertedFullFeature(
		_TempShapeFeatureList,
		_TempAppearanceFeatureList,
		TreeInstance, NodeIndex,
		TreeInstance.ParsedInstanceList.pElement + NodeIndex);

	ii = (int)_TempShapeFeatureList.Size;
	pd = pDeltaCoefficient_Shape + CoefficientOffsetList_Shape.pElement[NodeIndex];
	qd = _TempShapeFeatureList.pElement;
	for ( i = 0; i < ii; ++i ) *(pd++) += *(qd++) * Alpha;

	ii = (int)_TempAppearanceFeatureList.Size;
	pd = pDeltaCoefficient_Appearance + CoefficientOffsetList_Appearance.pElement[NodeIndex];
	qd = _TempAppearanceFeatureList.pElement;
	for ( i = 0; i < ii; ++i ) *(pd++) += *(qd++) * Alpha;

	const CAbstractNode* pNode = pTree->GetNode(NodeIndex);
	ii = pNode->GetChildrenAmount();
	const int* qci = pNode->GetPtrChildrenIndex();
	for ( i = 0; i < ii; ++i )
		_UpdateModel(pDeltaCoefficient_Shape, pDeltaCoefficient_Appearance, TreeInstance, *(qci++), Alpha);
}

